From 719a58270bce3cf32eb8bcf60f95f2110ad48c88 Mon Sep 17 00:00:00 2001 From: kilhyeonjun Date: Wed, 18 Mar 2026 12:21:08 +0900 Subject: [PATCH 01/63] fix(shared): respect cached model context limits for Anthropic providers post-GA After Anthropic's 1M context GA (2026-03-13), the beta header is no longer sent. The existing detection relied solely on the beta header to set anthropicContext1MEnabled, causing all Anthropic models to fall back to the 200K default despite models.dev reporting 1M. Update resolveActualContextLimit to check per-model cached limits from provider config (populated from models.dev data) when the explicit 1M flag is not set. Priority order: 1. Explicit 1M mode (beta header or env var) - all Anthropic models 2. Per-model cached limit from provider config 3. Default 200K fallback This preserves the #2460 fix (explicit 1M flag always wins over cached values) while allowing GA models to use their correct limits. Fixes premature context warnings at 140K and unnecessary compaction at 156K for opus-4-6 and sonnet-4-6 users without env var workaround. --- ...indow-monitor.model-context-limits.test.ts | 54 ++++++++++++++++--- src/shared/context-limit-resolver.test.ts | 39 ++++++++++++-- src/shared/context-limit-resolver.ts | 8 ++- 3 files changed, 89 insertions(+), 12 deletions(-) diff --git a/src/hooks/context-window-monitor.model-context-limits.test.ts b/src/hooks/context-window-monitor.model-context-limits.test.ts index 6dca2df5f..f9e2fe43e 100644 --- a/src/hooks/context-window-monitor.model-context-limits.test.ts +++ b/src/hooks/context-window-monitor.model-context-limits.test.ts @@ -136,8 +136,8 @@ describe("context-window-monitor modelContextLimitsCache", () => { }) describe("#given Anthropic provider with cached context limit and 1M mode disabled", () => { - describe("#when cached usage exceeds the Anthropic default limit", () => { - it("#then should ignore the cached limit and append the reminder from the default Anthropic limit", async () => { + describe("#when cached usage is below threshold of cached limit", () => { + it("#then should respect the cached limit and skip the reminder", async () => { // given const modelContextLimitsCache = new Map() modelContextLimitsCache.set("anthropic/claude-sonnet-4-5", 500000) @@ -146,7 +146,7 @@ describe("context-window-monitor modelContextLimitsCache", () => { anthropicContext1MEnabled: false, modelContextLimitsCache, }) - const sessionID = "ses_anthropic_default_overrides_cached_limit" + const sessionID = "ses_anthropic_cached_limit_respected" await hook.event({ event: { @@ -173,11 +173,51 @@ describe("context-window-monitor modelContextLimitsCache", () => { const output = createOutput() await hook["tool.execute.after"]({ tool: "bash", sessionID, callID: "call_1" }, output) - // then + // then — 160K/500K = 32%, well below 70% threshold + expect(output.output).toBe("original") + }) + }) + + describe("#when cached usage exceeds threshold of cached limit", () => { + it("#then should use the cached limit for the reminder", async () => { + // given + const modelContextLimitsCache = new Map() + modelContextLimitsCache.set("anthropic/claude-sonnet-4-5", 500000) + + const hook = createContextWindowMonitorHook({} as never, { + anthropicContext1MEnabled: false, + modelContextLimitsCache, + }) + const sessionID = "ses_anthropic_cached_limit_exceeded" + + await hook.event({ + event: { + type: "message.updated", + properties: { + info: { + role: "assistant", + sessionID, + providerID: "anthropic", + modelID: "claude-sonnet-4-5", + finish: true, + tokens: { + input: 350000, + output: 0, + reasoning: 0, + cache: { read: 10000, write: 0 }, + }, + }, + }, + }, + }) + + // when + const output = createOutput() + await hook["tool.execute.after"]({ tool: "bash", sessionID, callID: "call_1" }, output) + + // then — 360K/500K = 72%, above 70% threshold, uses cached 500K limit expect(output.output).toContain("context remaining") - expect(output.output).toContain("200,000-token context window") - expect(output.output).not.toContain("500,000-token context window") - expect(output.output).not.toContain("1,000,000-token context window") + expect(output.output).toContain("500,000-token context window") }) }) }) diff --git a/src/shared/context-limit-resolver.test.ts b/src/shared/context-limit-resolver.test.ts index 5ce62a8df..f08dd1d18 100644 --- a/src/shared/context-limit-resolver.test.ts +++ b/src/shared/context-limit-resolver.test.ts @@ -28,21 +28,52 @@ describe("resolveActualContextLimit", () => { resetContextLimitEnv() }) - it("returns the default Anthropic limit when 1M mode is disabled despite a cached limit", () => { + it("returns cached limit for Anthropic models when 1M mode is disabled (GA support)", () => { // given delete process.env[ANTHROPIC_CONTEXT_ENV_KEY] delete process.env[VERTEX_CONTEXT_ENV_KEY] const modelContextLimitsCache = new Map() - modelContextLimitsCache.set("anthropic/claude-sonnet-4-5", 123456) + modelContextLimitsCache.set("anthropic/claude-opus-4-6", 1_000_000) // when - const actualLimit = resolveActualContextLimit("anthropic", "claude-sonnet-4-5", { + const actualLimit = resolveActualContextLimit("anthropic", "claude-opus-4-6", { anthropicContext1MEnabled: false, modelContextLimitsCache, }) + // then — models.dev reports 1M for GA models, resolver should respect it + expect(actualLimit).toBe(1_000_000) + }) + + it("returns default 200K for Anthropic models without cached limit and 1M mode disabled", () => { + // given + delete process.env[ANTHROPIC_CONTEXT_ENV_KEY] + delete process.env[VERTEX_CONTEXT_ENV_KEY] + + // when + const actualLimit = resolveActualContextLimit("anthropic", "claude-sonnet-4-5", { + anthropicContext1MEnabled: false, + }) + // then - expect(actualLimit).toBe(200000) + expect(actualLimit).toBe(200_000) + }) + + it("explicit 1M mode takes priority over cached limit", () => { + // given + delete process.env[ANTHROPIC_CONTEXT_ENV_KEY] + delete process.env[VERTEX_CONTEXT_ENV_KEY] + const modelContextLimitsCache = new Map() + modelContextLimitsCache.set("anthropic/claude-sonnet-4-5", 200_000) + + // when + const actualLimit = resolveActualContextLimit("anthropic", "claude-sonnet-4-5", { + anthropicContext1MEnabled: true, + modelContextLimitsCache, + }) + + // then — explicit 1M flag overrides cached 200K + expect(actualLimit).toBe(1_000_000) }) it("treats Anthropics aliases as Anthropic providers", () => { diff --git a/src/shared/context-limit-resolver.ts b/src/shared/context-limit-resolver.ts index 361fa45d0..448127125 100644 --- a/src/shared/context-limit-resolver.ts +++ b/src/shared/context-limit-resolver.ts @@ -26,7 +26,13 @@ export function resolveActualContextLimit( modelCacheState?: ContextLimitModelCacheState, ): number | null { if (isAnthropicProvider(providerID)) { - return getAnthropicActualLimit(modelCacheState) + const explicit1M = getAnthropicActualLimit(modelCacheState) + if (explicit1M === 1_000_000) return explicit1M + + const cachedLimit = modelCacheState?.modelContextLimitsCache?.get(`${providerID}/${modelID}`) + if (cachedLimit) return cachedLimit + + return DEFAULT_ANTHROPIC_ACTUAL_LIMIT } return modelCacheState?.modelContextLimitsCache?.get(`${providerID}/${modelID}`) ?? null From 23a30e86f25d21152b7187aec6393fd8b577208b Mon Sep 17 00:00:00 2001 From: MoerAI Date: Fri, 20 Mar 2026 10:44:19 +0900 Subject: [PATCH 02/63] fix(windows): resolve symlinked config paths for plugin detection (fixes #2271) --- .../checks/system-loaded-version.test.ts | 27 +++++++- .../doctor/checks/system-loaded-version.ts | 21 ++++-- .../discovery.test.ts | 69 +++++++++++++++++++ .../claude-code-plugin-loader/discovery.ts | 22 +++++- src/shared/opencode-config-dir.ts | 21 ++++-- 5 files changed, 146 insertions(+), 14 deletions(-) create mode 100644 src/features/claude-code-plugin-loader/discovery.test.ts diff --git a/src/cli/doctor/checks/system-loaded-version.test.ts b/src/cli/doctor/checks/system-loaded-version.test.ts index 3a89ee82d..b35e5a638 100644 --- a/src/cli/doctor/checks/system-loaded-version.test.ts +++ b/src/cli/doctor/checks/system-loaded-version.test.ts @@ -1,5 +1,5 @@ import { afterEach, describe, expect, it } from "bun:test" -import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from "node:fs" +import { mkdirSync, mkdtempSync, realpathSync, rmSync, symlinkSync, writeFileSync } from "node:fs" import { tmpdir } from "node:os" import { dirname, join } from "node:path" @@ -104,6 +104,31 @@ describe("system loaded version", () => { expect(loadedVersion.expectedVersion).toBe("2.3.4") expect(loadedVersion.loadedVersion).toBe("2.3.4") }) + + it("resolves symlinked config directories before selecting install path", () => { + //#given + const realConfigDir = createTemporaryDirectory("omo-real-config-") + const symlinkBaseDir = createTemporaryDirectory("omo-symlink-base-") + const symlinkConfigDir = join(symlinkBaseDir, "config-link") + + symlinkSync(realConfigDir, symlinkConfigDir, process.platform === "win32" ? "junction" : "dir") + process.env.OPENCODE_CONFIG_DIR = symlinkConfigDir + + writeJson(join(realConfigDir, "package.json"), { + dependencies: { [PACKAGE_NAME]: "4.5.6" }, + }) + writeJson(join(realConfigDir, "node_modules", PACKAGE_NAME, "package.json"), { + version: "4.5.6", + }) + + //#when + const loadedVersion = getLoadedPluginVersion() + + //#then + expect(loadedVersion.cacheDir).toBe(realpathSync(symlinkConfigDir)) + expect(loadedVersion.expectedVersion).toBe("4.5.6") + expect(loadedVersion.loadedVersion).toBe("4.5.6") + }) }) describe("getSuggestedInstallTag", () => { diff --git a/src/cli/doctor/checks/system-loaded-version.ts b/src/cli/doctor/checks/system-loaded-version.ts index a62c0f97a..7693b2d7a 100644 --- a/src/cli/doctor/checks/system-loaded-version.ts +++ b/src/cli/doctor/checks/system-loaded-version.ts @@ -1,4 +1,4 @@ -import { existsSync, readFileSync } from "node:fs" +import { existsSync, readFileSync, realpathSync } from "node:fs" import { homedir } from "node:os" import { join } from "node:path" @@ -36,6 +36,16 @@ function resolveOpenCodeCacheDir(): string { return platformDefault } +function resolveExistingDir(dirPath: string): string { + if (!existsSync(dirPath)) return dirPath + + try { + return realpathSync(dirPath) + } catch { + return dirPath + } +} + function readPackageJson(filePath: string): PackageJsonShape | null { if (!existsSync(filePath)) return null @@ -55,12 +65,13 @@ function normalizeVersion(value: string | undefined): string | null { export function getLoadedPluginVersion(): LoadedVersionInfo { const configPaths = getOpenCodeConfigPaths({ binary: "opencode" }) - const cacheDir = resolveOpenCodeCacheDir() + const configDir = resolveExistingDir(configPaths.configDir) + const cacheDir = resolveExistingDir(resolveOpenCodeCacheDir()) const candidates = [ { - cacheDir: configPaths.configDir, - cachePackagePath: configPaths.packageJson, - installedPackagePath: join(configPaths.configDir, "node_modules", PACKAGE_NAME, "package.json"), + cacheDir: configDir, + cachePackagePath: join(configDir, "package.json"), + installedPackagePath: join(configDir, "node_modules", PACKAGE_NAME, "package.json"), }, { cacheDir, diff --git a/src/features/claude-code-plugin-loader/discovery.test.ts b/src/features/claude-code-plugin-loader/discovery.test.ts new file mode 100644 index 000000000..dab25cd22 --- /dev/null +++ b/src/features/claude-code-plugin-loader/discovery.test.ts @@ -0,0 +1,69 @@ +import { afterEach, beforeEach, describe, expect, it } from "bun:test" +import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from "node:fs" +import { tmpdir } from "node:os" +import { join } from "node:path" + +import { discoverInstalledPlugins } from "./discovery" + +const originalClaudePluginsHome = process.env.CLAUDE_PLUGINS_HOME +const temporaryDirectories: string[] = [] + +function createTemporaryDirectory(prefix: string): string { + const directory = mkdtempSync(join(tmpdir(), prefix)) + temporaryDirectories.push(directory) + return directory +} + +describe("discoverInstalledPlugins", () => { + beforeEach(() => { + const pluginsHome = createTemporaryDirectory("omo-claude-plugins-") + process.env.CLAUDE_PLUGINS_HOME = pluginsHome + }) + + afterEach(() => { + if (originalClaudePluginsHome === undefined) { + delete process.env.CLAUDE_PLUGINS_HOME + } else { + process.env.CLAUDE_PLUGINS_HOME = originalClaudePluginsHome + } + + for (const directory of temporaryDirectories.splice(0)) { + rmSync(directory, { recursive: true, force: true }) + } + }) + + it("derives package name from file URL plugin keys", () => { + //#given + const pluginsHome = process.env.CLAUDE_PLUGINS_HOME as string + const installPath = join(createTemporaryDirectory("omo-plugin-install-"), "oh-my-opencode") + mkdirSync(installPath, { recursive: true }) + + const databasePath = join(pluginsHome, "installed_plugins.json") + writeFileSync( + databasePath, + JSON.stringify({ + version: 2, + plugins: { + "file:///D:/configs/user-configs/.config/opencode/node_modules/oh-my-opencode@latest": [ + { + scope: "user", + installPath, + version: "3.10.0", + installedAt: "2026-03-20T00:00:00Z", + lastUpdated: "2026-03-20T00:00:00Z", + }, + ], + }, + }), + "utf-8", + ) + + //#when + const discovered = discoverInstalledPlugins() + + //#then + expect(discovered.errors).toHaveLength(0) + expect(discovered.plugins).toHaveLength(1) + expect(discovered.plugins[0]?.name).toBe("oh-my-opencode") + }) +}) diff --git a/src/features/claude-code-plugin-loader/discovery.ts b/src/features/claude-code-plugin-loader/discovery.ts index f2ec3d0be..217e7211e 100644 --- a/src/features/claude-code-plugin-loader/discovery.ts +++ b/src/features/claude-code-plugin-loader/discovery.ts @@ -1,6 +1,7 @@ import { existsSync, readFileSync } from "fs" import { homedir } from "os" -import { join } from "path" +import { basename, join } from "path" +import { fileURLToPath } from "url" import { log } from "../../shared/logger" import type { InstalledPluginsDatabase, @@ -79,8 +80,23 @@ function loadPluginManifest(installPath: string): PluginManifest | null { } function derivePluginNameFromKey(pluginKey: string): string { - const atIndex = pluginKey.indexOf("@") - return atIndex > 0 ? pluginKey.substring(0, atIndex) : pluginKey + const keyWithoutSource = pluginKey.startsWith("npm:") ? pluginKey.slice(4) : pluginKey + const versionSeparator = keyWithoutSource.lastIndexOf("@") + const keyWithoutVersion = versionSeparator > 0 ? keyWithoutSource.slice(0, versionSeparator) : keyWithoutSource + + if (keyWithoutVersion.startsWith("file://")) { + try { + return basename(fileURLToPath(keyWithoutVersion)) + } catch { + return basename(keyWithoutVersion) + } + } + + if (keyWithoutVersion.includes("/") || keyWithoutVersion.includes("\\")) { + return basename(keyWithoutVersion) + } + + return keyWithoutVersion } function isPluginEnabled( diff --git a/src/shared/opencode-config-dir.ts b/src/shared/opencode-config-dir.ts index 620d561dd..cf4fc28da 100644 --- a/src/shared/opencode-config-dir.ts +++ b/src/shared/opencode-config-dir.ts @@ -1,4 +1,4 @@ -import { existsSync } from "node:fs" +import { existsSync, realpathSync } from "node:fs" import { homedir } from "node:os" import { join, resolve } from "node:path" @@ -42,14 +42,25 @@ function getTauriConfigDir(identifier: string): string { } } +function resolveConfigPath(pathValue: string): string { + const resolvedPath = resolve(pathValue) + if (!existsSync(resolvedPath)) return resolvedPath + + try { + return realpathSync(resolvedPath) + } catch { + return resolvedPath + } +} + function getCliConfigDir(): string { const envConfigDir = process.env.OPENCODE_CONFIG_DIR?.trim() if (envConfigDir) { - return resolve(envConfigDir) + return resolveConfigPath(envConfigDir) } const xdgConfig = process.env.XDG_CONFIG_HOME || join(homedir(), ".config") - return join(xdgConfig, "opencode") + return resolveConfigPath(join(xdgConfig, "opencode")) } export function getOpenCodeConfigDir(options: OpenCodeConfigDirOptions): string { @@ -60,7 +71,7 @@ export function getOpenCodeConfigDir(options: OpenCodeConfigDirOptions): string } const identifier = isDevBuild(version) ? TAURI_APP_IDENTIFIER_DEV : TAURI_APP_IDENTIFIER - const tauriDir = getTauriConfigDir(identifier) + const tauriDir = resolveConfigPath(getTauriConfigDir(identifier)) if (checkExisting) { const legacyDir = getCliConfigDir() @@ -92,7 +103,7 @@ export function detectExistingConfigDir(binary: OpenCodeBinaryType, version?: st const envConfigDir = process.env.OPENCODE_CONFIG_DIR?.trim() if (envConfigDir) { - locations.push(resolve(envConfigDir)) + locations.push(resolveConfigPath(envConfigDir)) } if (binary === "opencode-desktop") { From 3773e370ec278cfb551e3246b4b698484549f09c Mon Sep 17 00:00:00 2001 From: MoerAI Date: Fri, 20 Mar 2026 11:00:00 +0900 Subject: [PATCH 03/63] fix(runtime-fallback): detect bare 429 rate-limit signals (fixes #2677) --- .../runtime-fallback/error-classifier.test.ts | 14 ++++++++++ .../runtime-fallback/error-classifier.ts | 2 +- .../message-update-handler.test.ts | 26 +++++++++++++++++++ 3 files changed, 41 insertions(+), 1 deletion(-) diff --git a/src/hooks/runtime-fallback/error-classifier.test.ts b/src/hooks/runtime-fallback/error-classifier.test.ts index 954e28758..5dd772e2b 100644 --- a/src/hooks/runtime-fallback/error-classifier.test.ts +++ b/src/hooks/runtime-fallback/error-classifier.test.ts @@ -31,6 +31,20 @@ describe("runtime-fallback error classifier", () => { expect(signal).toBeDefined() }) + test("detects too-many-requests auto-retry status signals without countdown text", () => { + //#given + const info = { + status: + "Too Many Requests: Sorry, you've exhausted this model's rate limit. Please try a different model.", + } + + //#when + const signal = extractAutoRetrySignal(info) + + //#then + expect(signal).toBeDefined() + }) + test("treats cooling-down retry messages as retryable", () => { //#given const error = { diff --git a/src/hooks/runtime-fallback/error-classifier.ts b/src/hooks/runtime-fallback/error-classifier.ts index e581f3fb8..cd7aa41f7 100644 --- a/src/hooks/runtime-fallback/error-classifier.ts +++ b/src/hooks/runtime-fallback/error-classifier.ts @@ -145,7 +145,7 @@ export function extractAutoRetrySignal(info: Record | undefined const combined = candidates.join("\n") if (!combined) return undefined - const isAutoRetry = AUTO_RETRY_PATTERNS.every((test) => test(combined)) + const isAutoRetry = AUTO_RETRY_PATTERNS.some((test) => test(combined)) if (isAutoRetry) { return { signal: combined } } diff --git a/src/hooks/runtime-fallback/message-update-handler.test.ts b/src/hooks/runtime-fallback/message-update-handler.test.ts index 7a3142e68..52a91abbe 100644 --- a/src/hooks/runtime-fallback/message-update-handler.test.ts +++ b/src/hooks/runtime-fallback/message-update-handler.test.ts @@ -1,6 +1,7 @@ import { describe, expect, it } from "bun:test" import type { RuntimeFallbackPluginInput } from "./types" import { hasVisibleAssistantResponse } from "./visible-assistant-response" +import { extractAutoRetrySignal } from "./error-classifier" function createContext(messagesResponse: unknown): RuntimeFallbackPluginInput { return { @@ -53,4 +54,29 @@ describe("hasVisibleAssistantResponse", () => { // then expect(result).toBe(true) }) + + it("#given a too-many-requests assistant reply #when visibility is checked #then it is treated as an auto-retry signal", async () => { + // given + const checkVisibleResponse = hasVisibleAssistantResponse(extractAutoRetrySignal) + const ctx = createContext({ + data: [ + { info: { role: "user" }, parts: [{ type: "text", text: "latest question" }] }, + { + info: { role: "assistant" }, + parts: [ + { + type: "text", + text: "Too Many Requests: Sorry, you've exhausted this model's rate limit. Please try a different model.", + }, + ], + }, + ], + }) + + // when + const result = await checkVisibleResponse(ctx, "session-rate-limit", undefined) + + // then + expect(result).toBe(false) + }) }) From 0d525192939c0c513b8cc6f06b47f6a42fbea121 Mon Sep 17 00:00:00 2001 From: PR Bot Date: Sat, 21 Mar 2026 01:29:53 +0800 Subject: [PATCH 04/63] feat: upgrade MiniMax from M2.5 to M2.7 and expand to more agents/categories MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Upgrade minimax-m2.5 → minimax-m2.7 (latest model) across all agents and categories - Replace minimax-m2.5-free with minimax-m2.7-highspeed (optimized speed variant) - Expand MiniMax fallback coverage to atlas, sisyphus-junior, writing, and unspecified-low - Add isMiniMaxModel() detection function in types.ts for model family detection - Update all tests (58 passing) and documentation --- docs/guide/agent-model-matching.md | 19 +++++++------ docs/guide/installation.md | 16 +++++------ docs/guide/overview.md | 2 +- docs/reference/configuration.md | 10 +++---- docs/reference/features.md | 4 +-- src/agents/AGENTS.md | 4 +-- src/agents/types.test.ts | 24 +++++++++++++++- src/agents/types.ts | 5 ++++ src/cli/model-fallback.ts | 4 +-- src/cli/openai-only-model-catalog.test.ts | 4 +-- src/shared/model-requirements.test.ts | 34 +++++++++++++++-------- src/shared/model-requirements.ts | 14 ++++++---- 12 files changed, 91 insertions(+), 49 deletions(-) diff --git a/docs/guide/agent-model-matching.md b/docs/guide/agent-model-matching.md index ec040b636..ad1f80b7d 100644 --- a/docs/guide/agent-model-matching.md +++ b/docs/guide/agent-model-matching.md @@ -92,10 +92,10 @@ These agents do grep, search, and retrieval. They intentionally use the fastest, | Agent | Role | Fallback Chain | Notes | | --------------------- | ------------------ | ---------------------------------------------- | ----------------------------------------------------- | -| **Explore** | Fast codebase grep | Grok Code Fast → opencode-go/minimax-m2.5 → MiniMax Free → Haiku → GPT-5-Nano | Speed is everything. Fire 10 in parallel. | -| **Librarian** | Docs/code search | opencode-go/minimax-m2.5 → MiniMax Free → Haiku → GPT-5-Nano | Doc retrieval doesn't need deep reasoning. | -| **Multimodal Looker** | Vision/screenshots | GPT-5.4 → opencode-go/kimi-k2.5 → GLM-4.6v → GPT-5-Nano | Uses the first available multimodal-capable fallback. | -| **Sisyphus-Junior** | Category executor | Claude Sonnet → opencode-go/kimi-k2.5 → GPT-5.4 → Big Pickle | Handles delegated category tasks. Sonnet-tier default. | +| **Explore** | Fast codebase grep | Grok Code Fast → opencode-go/minimax-m2.7-highspeed → MiniMax M2.7 → Haiku → GPT-5-Nano | Speed is everything. Fire 10 in parallel. | +| **Librarian** | Docs/code search | opencode-go/minimax-m2.7 → MiniMax M2.7-highspeed → Haiku → GPT-5-Nano | Doc retrieval doesn't need deep reasoning. | +| **Multimodal Looker** | Vision/screenshots | GPT-5.4 → opencode-go/kimi-k2.5 → GLM-4.6v → GPT-5-Nano | Uses the first available multimodal-capable fallback. | +| **Sisyphus-Junior** | Category executor | Claude Sonnet → opencode-go/kimi-k2.5 → GPT-5.4 → MiniMax M2.7 → Big Pickle | Handles delegated category tasks. Sonnet-tier default. | --- @@ -131,7 +131,8 @@ Principle-driven, explicit reasoning, deep technical capability. Best for agents | **Gemini 3.1 Pro** | Excels at visual/frontend tasks. Different reasoning style. Default for `visual-engineering` and `artistry`. | | **Gemini 3 Flash** | Fast. Good for doc search and light tasks. | | **Grok Code Fast 1** | Blazing fast code grep. Default for Explore agent. | -| **MiniMax M2.5** | Fast and smart. Good for utility tasks and search/retrieval. | +| **MiniMax M2.7** | Fast and smart. Good for utility tasks and search/retrieval. Upgraded from M2.5 with better reasoning. | +| **MiniMax M2.7 Highspeed** | Ultra-fast variant. Optimized for latency-sensitive tasks like codebase grep. | ### OpenCode Go @@ -143,11 +144,11 @@ A premium subscription tier ($10/month) that provides reliable access to Chinese | ------------------------ | --------------------------------------------------------------------- | | **opencode-go/kimi-k2.5** | Vision-capable, Claude-like reasoning. Used by Sisyphus, Atlas, Sisyphus-Junior, Multimodal Looker. | | **opencode-go/glm-5** | Text-only orchestration model. Used by Oracle, Prometheus, Metis, Momus. | -| **opencode-go/minimax-m2.5** | Ultra-cheap, fast responses. Used by Librarian, Explore for utility work. | +| **opencode-go/minimax-m2.7** | Ultra-cheap, fast responses. Used by Librarian, Explore, Atlas, Sisyphus-Junior for utility work. | **When It Gets Used:** -OpenCode Go models appear in fallback chains as intermediate options. They bridge the gap between premium Claude access and free-tier alternatives. The system tries OpenCode Go models before falling back to free tiers (MiniMax Free, Big Pickle) or GPT alternatives. +OpenCode Go models appear in fallback chains as intermediate options. They bridge the gap between premium Claude access and free-tier alternatives. The system tries OpenCode Go models before falling back to free tiers (MiniMax M2.7-highspeed, Big Pickle) or GPT alternatives. **Go-Only Scenarios:** @@ -155,7 +156,7 @@ Some model identifiers like `k2p5` (paid Kimi K2.5) and `glm-5` may only be avai ### About Free-Tier Fallbacks -You may see model names like `kimi-k2.5-free`, `minimax-m2.5-free`, or `big-pickle` (GLM 4.6) in the source code or logs. These are free-tier versions of the same model families, served through the OpenCode Zen provider. They exist as lower-priority entries in fallback chains. +You may see model names like `kimi-k2.5-free`, `minimax-m2.7-highspeed`, or `big-pickle` (GLM 4.6) in the source code or logs. These are free-tier or speed-optimized versions of the same model families. They exist as lower-priority entries in fallback chains. You don't need to configure them. The system includes them so it degrades gracefully when you don't have every paid subscription. If you have the paid version, the paid version is always preferred. @@ -171,7 +172,7 @@ When agents delegate work, they don't pick a model name — they pick a **catego | `ultrabrain` | Maximum reasoning needed | GPT-5.4 → Gemini 3.1 Pro → Claude Opus → opencode-go/glm-5 | | `deep` | Deep coding, complex logic | GPT-5.3 Codex → Claude Opus → Gemini 3.1 Pro | | `artistry` | Creative, novel approaches | Gemini 3.1 Pro → Claude Opus → GPT-5.4 | -| `quick` | Simple, fast tasks | GPT-5.4 Mini → Claude Haiku → Gemini Flash → opencode-go/minimax-m2.5 → GPT-5-Nano | +| `quick` | Simple, fast tasks | GPT-5.4 Mini → Claude Haiku → Gemini Flash → opencode-go/minimax-m2.7 → GPT-5-Nano | | `unspecified-high` | General complex work | Claude Opus → GPT-5.4 → GLM 5 → K2P5 → opencode-go/glm-5 → Kimi K2.5 | | `unspecified-low` | General standard work | Claude Sonnet → GPT-5.3 Codex → opencode-go/kimi-k2.5 → Gemini Flash | | `writing` | Text, docs, prose | Gemini Flash → opencode-go/kimi-k2.5 → Claude Sonnet | diff --git a/docs/guide/installation.md b/docs/guide/installation.md index edef25592..fd1a2367b 100644 --- a/docs/guide/installation.md +++ b/docs/guide/installation.md @@ -69,7 +69,7 @@ Ask the user these questions to determine CLI options: - If **no** → `--zai-coding-plan=no` (default) 7. **Do you have an OpenCode Go subscription?** - - OpenCode Go is a $10/month subscription providing access to GLM-5, Kimi K2.5, and MiniMax M2.5 models + - OpenCode Go is a $10/month subscription providing access to GLM-5, Kimi K2.5, and MiniMax M2.7 models - If **yes** → `--opencode-go=yes` - If **no** → `--opencode-go=no` (default) @@ -227,7 +227,7 @@ If Z.ai is your main provider, the most important fallbacks are: #### OpenCode Zen -OpenCode Zen provides access to `opencode/` prefixed models including `opencode/claude-opus-4-6`, `opencode/gpt-5.4`, `opencode/gpt-5.3-codex`, `opencode/gpt-5-nano`, `opencode/glm-5`, `opencode/big-pickle`, and `opencode/minimax-m2.5-free`. +OpenCode Zen provides access to `opencode/` prefixed models including `opencode/claude-opus-4-6`, `opencode/gpt-5.4`, `opencode/gpt-5.3-codex`, `opencode/gpt-5-nano`, `opencode/glm-5`, `opencode/big-pickle`, and `opencode/minimax-m2.7-highspeed`. When OpenCode Zen is the best available provider (no native or Copilot), these models are used: @@ -236,7 +236,7 @@ When OpenCode Zen is the best available provider (no native or Copilot), these m | **Sisyphus** | `opencode/claude-opus-4-6` | | **Oracle** | `opencode/gpt-5.4` | | **Explore** | `opencode/gpt-5-nano` | -| **Librarian** | `opencode/minimax-m2.5-free` / `opencode/big-pickle` | +| **Librarian** | `opencode/minimax-m2.7-highspeed` / `opencode/big-pickle` | ##### Setup @@ -296,8 +296,8 @@ Not all models behave the same way. Understanding which models are "similar" hel | --------------------- | -------------------------------- | ----------------------------------------------------------- | | **Gemini 3 Pro** | google, github-copilot, opencode | Excels at visual/frontend tasks. Different reasoning style. | | **Gemini 3 Flash** | google, github-copilot, opencode | Fast, good for doc search and light tasks. | -| **MiniMax M2.5** | venice | Fast and smart. Good for utility tasks. | -| **MiniMax M2.5 Free** | opencode | Free-tier MiniMax. Fast for search/retrieval. | +| **MiniMax M2.7** | venice, opencode-go | Fast and smart. Good for utility tasks. Upgraded from M2.5. | +| **MiniMax M2.7 Highspeed** | opencode | Ultra-fast MiniMax variant. Optimized for latency. | **Speed-Focused Models**: @@ -305,7 +305,7 @@ Not all models behave the same way. Understanding which models are "similar" hel | ----------------------- | ---------------------- | -------------- | --------------------------------------------------------------------------------------------------------------------------------------------- | | **Grok Code Fast 1** | github-copilot, venice | Very fast | Optimized for code grep/search. Default for Explore. | | **Claude Haiku 4.5** | anthropic, opencode | Fast | Good balance of speed and intelligence. | -| **MiniMax M2.5 (Free)** | opencode, venice | Fast | Smart for its speed class. | +| **MiniMax M2.7 Highspeed** | opencode | Very fast | Ultra-fast MiniMax variant. Smart for its speed class. | | **GPT-5.3-codex-spark** | openai | Extremely fast | Blazing fast but compacts so aggressively that oh-my-opencode's context management doesn't work well with it. Not recommended for omo agents. | #### What Each Agent Does and Which Model It Got @@ -344,8 +344,8 @@ These agents do search, grep, and retrieval. They intentionally use fast, cheap | Agent | Role | Default Chain | Design Rationale | | --------------------- | ------------------ | ---------------------------------------------------------------------- | -------------------------------------------------------------- | -| **Explore** | Fast codebase grep | MiniMax M2.5 Free → Grok Code Fast → MiniMax M2.5 → Haiku → GPT-5-Nano | Speed is everything. Grok is blazing fast for grep. | -| **Librarian** | Docs/code search | MiniMax M2.5 Free → Gemini Flash → Big Pickle | Entirely free-tier. Doc retrieval doesn't need deep reasoning. | +| **Explore** | Fast codebase grep | Grok Code Fast → MiniMax M2.7-highspeed → MiniMax M2.7 → Haiku → GPT-5-Nano | Speed is everything. Grok is blazing fast for grep. | +| **Librarian** | Docs/code search | MiniMax M2.7 → MiniMax M2.7-highspeed → Haiku → GPT-5-Nano | Doc retrieval doesn't need deep reasoning. MiniMax is fast. | | **Multimodal Looker** | Vision/screenshots | Kimi K2.5 → Kimi Free → Gemini Flash → GPT-5.4 → GLM-4.6v | Kimi excels at multimodal understanding. | #### Why Different Models Need Different Prompts diff --git a/docs/guide/overview.md b/docs/guide/overview.md index 78f34937f..3d44d543e 100644 --- a/docs/guide/overview.md +++ b/docs/guide/overview.md @@ -221,7 +221,7 @@ You can override specific agents or categories in your config: **Different-behavior models**: - Gemini 3 Pro — excels at visual/frontend tasks -- MiniMax M2.5 — fast and smart for utility tasks +- MiniMax M2.7 / M2.7-highspeed — fast and smart for utility tasks - Grok Code Fast 1 — optimized for code grep/search See the [Agent-Model Matching Guide](./agent-model-matching.md) for complete details on which models work best for each agent, safe vs dangerous overrides, and provider priority chains. diff --git a/docs/reference/configuration.md b/docs/reference/configuration.md index bfa80ed5a..f39760322 100644 --- a/docs/reference/configuration.md +++ b/docs/reference/configuration.md @@ -270,8 +270,8 @@ Disable categories: `{ "disabled_categories": ["ultrabrain"] }` | **Sisyphus** | `claude-opus-4-6` | `claude-opus-4-6` → `glm-5` → `big-pickle` | | **Hephaestus** | `gpt-5.3-codex` | `gpt-5.3-codex` → `gpt-5.4` (GitHub Copilot fallback) | | **oracle** | `gpt-5.4` | `gpt-5.4` → `gemini-3.1-pro` → `claude-opus-4-6` | -| **librarian** | `gemini-3-flash` | `gemini-3-flash` → `minimax-m2.5-free` → `big-pickle` | -| **explore** | `grok-code-fast-1` | `grok-code-fast-1` → `minimax-m2.5-free` → `claude-haiku-4-5` → `gpt-5-nano` | +| **librarian** | `minimax-m2.7` | `minimax-m2.7` → `minimax-m2.7-highspeed` → `claude-haiku-4-5` → `gpt-5-nano` | +| **explore** | `grok-code-fast-1` | `grok-code-fast-1` → `minimax-m2.7-highspeed` → `minimax-m2.7` → `claude-haiku-4-5` → `gpt-5-nano` | | **multimodal-looker** | `gpt-5.3-codex` | `gpt-5.3-codex` → `k2p5` → `gemini-3-flash` → `glm-4.6v` → `gpt-5-nano` | | **Prometheus** | `claude-opus-4-6` | `claude-opus-4-6` → `gpt-5.4` → `gemini-3.1-pro` | | **Metis** | `claude-opus-4-6` | `claude-opus-4-6` → `gpt-5.4` → `gemini-3.1-pro` | @@ -286,10 +286,10 @@ Disable categories: `{ "disabled_categories": ["ultrabrain"] }` | **ultrabrain** | `gpt-5.4` | `gpt-5.4` → `gemini-3.1-pro` → `claude-opus-4-6` | | **deep** | `gpt-5.3-codex` | `gpt-5.3-codex` → `claude-opus-4-6` → `gemini-3.1-pro` | | **artistry** | `gemini-3.1-pro` | `gemini-3.1-pro` → `claude-opus-4-6` → `gpt-5.4` | -| **quick** | `gpt-5.4-mini` | `gpt-5.4-mini` → `claude-haiku-4-5` → `gemini-3-flash` → `minimax-m2.5` → `gpt-5-nano` | -| **unspecified-low** | `claude-sonnet-4-6` | `claude-sonnet-4-6` → `gpt-5.3-codex` → `gemini-3-flash` | +| **quick** | `gpt-5.4-mini` | `gpt-5.4-mini` → `claude-haiku-4-5` → `gemini-3-flash` → `minimax-m2.7` → `gpt-5-nano` | +| **unspecified-low** | `claude-sonnet-4-6` | `claude-sonnet-4-6` → `gpt-5.3-codex` → `gemini-3-flash` → `minimax-m2.7` | | **unspecified-high** | `claude-opus-4-6` | `claude-opus-4-6` → `gpt-5.4 (high)` → `glm-5` → `k2p5` → `kimi-k2.5` | -| **writing** | `gemini-3-flash` | `gemini-3-flash` → `claude-sonnet-4-6` | +| **writing** | `gemini-3-flash` | `gemini-3-flash` → `claude-sonnet-4-6` → `minimax-m2.7` | Run `bunx oh-my-opencode doctor --verbose` to see effective model resolution for your config. diff --git a/docs/reference/features.md b/docs/reference/features.md index 09082dc3b..5240424b4 100644 --- a/docs/reference/features.md +++ b/docs/reference/features.md @@ -11,8 +11,8 @@ Oh-My-OpenCode provides 11 specialized AI agents. Each has distinct expertise, o | **Sisyphus** | `claude-opus-4-6` | The default orchestrator. Plans, delegates, and executes complex tasks using specialized subagents with aggressive parallel execution. Todo-driven workflow with extended thinking (32k budget). Fallback: `glm-5` → `big-pickle`. | | **Hephaestus** | `gpt-5.3-codex` | The Legitimate Craftsman. Autonomous deep worker inspired by AmpCode's deep mode. Goal-oriented execution with thorough research before action. Explores codebase patterns, completes tasks end-to-end without premature stopping. Named after the Greek god of forge and craftsmanship. Fallback: `gpt-5.4` on GitHub Copilot. Requires a GPT-capable provider. | | **Oracle** | `gpt-5.4` | Architecture decisions, code review, debugging. Read-only consultation with stellar logical reasoning and deep analysis. Inspired by AmpCode. Fallback: `gemini-3.1-pro` → `claude-opus-4-6`. | -| **Librarian** | `gemini-3-flash` | Multi-repo analysis, documentation lookup, OSS implementation examples. Deep codebase understanding with evidence-based answers. Fallback: `minimax-m2.5-free` → `big-pickle`. | -| **Explore** | `grok-code-fast-1` | Fast codebase exploration and contextual grep. Fallback: `minimax-m2.5-free` → `claude-haiku-4-5` → `gpt-5-nano`. | +| **Librarian** | `minimax-m2.7` | Multi-repo analysis, documentation lookup, OSS implementation examples. Deep codebase understanding with evidence-based answers. Fallback: `minimax-m2.7-highspeed` → `claude-haiku-4-5` → `gpt-5-nano`. | +| **Explore** | `grok-code-fast-1` | Fast codebase exploration and contextual grep. Fallback: `minimax-m2.7-highspeed` → `minimax-m2.7` → `claude-haiku-4-5` → `gpt-5-nano`. | | **Multimodal-Looker** | `gpt-5.3-codex` | Visual content specialist. Analyzes PDFs, images, diagrams to extract information. Fallback: `k2p5` → `gemini-3-flash` → `glm-4.6v` → `gpt-5-nano`. | ### Planning Agents diff --git a/src/agents/AGENTS.md b/src/agents/AGENTS.md index a4dcf175a..49b7830f2 100644 --- a/src/agents/AGENTS.md +++ b/src/agents/AGENTS.md @@ -13,8 +13,8 @@ Agent factories following `createXXXAgent(model) → AgentConfig` pattern. Each | **Sisyphus** | claude-opus-4-6 max | 0.1 | all | k2p5 → kimi-k2.5 → gpt-5.4 medium → glm-5 → big-pickle | Main orchestrator, plans + delegates | | **Hephaestus** | gpt-5.3-codex medium | 0.1 | all | gpt-5.4 medium (copilot) | Autonomous deep worker | | **Oracle** | gpt-5.4 high | 0.1 | subagent | gemini-3.1-pro high → claude-opus-4-6 max | Read-only consultation | -| **Librarian** | gemini-3-flash | 0.1 | subagent | minimax-m2.5-free → big-pickle | External docs/code search | -| **Explore** | grok-code-fast-1 | 0.1 | subagent | minimax-m2.5-free → claude-haiku-4-5 → gpt-5-nano | Contextual grep | +| **Librarian** | minimax-m2.7 | 0.1 | subagent | minimax-m2.7-highspeed → claude-haiku-4-5 → gpt-5-nano | External docs/code search | +| **Explore** | grok-code-fast-1 | 0.1 | subagent | minimax-m2.7-highspeed → minimax-m2.7 → claude-haiku-4-5 → gpt-5-nano | Contextual grep | | **Multimodal-Looker** | gpt-5.3-codex medium | 0.1 | subagent | k2p5 → gemini-3-flash → glm-4.6v → gpt-5-nano | PDF/image analysis | | **Metis** | claude-opus-4-6 max | **0.3** | subagent | gpt-5.4 high → gemini-3.1-pro high | Pre-planning consultant | | **Momus** | gpt-5.4 xhigh | 0.1 | subagent | claude-opus-4-6 max → gemini-3.1-pro high | Plan reviewer | diff --git a/src/agents/types.test.ts b/src/agents/types.test.ts index 5d712fc94..c911324fd 100644 --- a/src/agents/types.test.ts +++ b/src/agents/types.test.ts @@ -1,5 +1,5 @@ import { describe, test, expect } from "bun:test"; -import { isGptModel, isGeminiModel, isGpt5_4Model } from "./types"; +import { isGptModel, isGeminiModel, isGpt5_4Model, isMiniMaxModel } from "./types"; describe("isGpt5_4Model", () => { test("detects gpt-5.4 models", () => { @@ -79,6 +79,28 @@ describe("isGptModel", () => { }); }); +describe("isMiniMaxModel", () => { + test("detects minimax models with provider prefix", () => { + expect(isMiniMaxModel("opencode-go/minimax-m2.7")).toBe(true); + expect(isMiniMaxModel("opencode/minimax-m2.7-highspeed")).toBe(true); + expect(isMiniMaxModel("opencode-go/minimax-m2.5")).toBe(true); + expect(isMiniMaxModel("opencode/minimax-m2.5-free")).toBe(true); + }); + + test("detects minimax models without provider prefix", () => { + expect(isMiniMaxModel("minimax-m2.7")).toBe(true); + expect(isMiniMaxModel("minimax-m2.7-highspeed")).toBe(true); + expect(isMiniMaxModel("minimax-m2.5")).toBe(true); + }); + + test("does not match non-minimax models", () => { + expect(isMiniMaxModel("openai/gpt-5.4")).toBe(false); + expect(isMiniMaxModel("anthropic/claude-opus-4-6")).toBe(false); + expect(isMiniMaxModel("google/gemini-3.1-pro")).toBe(false); + expect(isMiniMaxModel("opencode-go/kimi-k2.5")).toBe(false); + }); +}); + describe("isGeminiModel", () => { test("#given google provider models #then returns true", () => { expect(isGeminiModel("google/gemini-3.1-pro")).toBe(true); diff --git a/src/agents/types.ts b/src/agents/types.ts index acf490007..6a21156f8 100644 --- a/src/agents/types.ts +++ b/src/agents/types.ts @@ -91,6 +91,11 @@ export function isGpt5_3CodexModel(model: string): boolean { const GEMINI_PROVIDERS = ["google/", "google-vertex/"]; +export function isMiniMaxModel(model: string): boolean { + const modelName = extractModelName(model).toLowerCase(); + return modelName.includes("minimax"); +} + export function isGeminiModel(model: string): boolean { if (GEMINI_PROVIDERS.some((prefix) => model.startsWith(prefix))) return true; diff --git a/src/cli/model-fallback.ts b/src/cli/model-fallback.ts index f0b044978..331c97142 100644 --- a/src/cli/model-fallback.ts +++ b/src/cli/model-fallback.ts @@ -55,7 +55,7 @@ export function generateModelConfig(config: InstallConfig): GeneratedOmoConfig { for (const [role, req] of Object.entries(CLI_AGENT_MODEL_REQUIREMENTS)) { if (role === "librarian") { if (avail.opencodeGo) { - agents[role] = { model: "opencode-go/minimax-m2.5" } + agents[role] = { model: "opencode-go/minimax-m2.7" } } else if (avail.zai) { agents[role] = { model: ZAI_MODEL } } @@ -68,7 +68,7 @@ export function generateModelConfig(config: InstallConfig): GeneratedOmoConfig { } else if (avail.opencodeZen) { agents[role] = { model: "opencode/claude-haiku-4-5" } } else if (avail.opencodeGo) { - agents[role] = { model: "opencode-go/minimax-m2.5" } + agents[role] = { model: "opencode-go/minimax-m2.7" } } else if (avail.copilot) { agents[role] = { model: "github-copilot/gpt-5-mini" } } else { diff --git a/src/cli/openai-only-model-catalog.test.ts b/src/cli/openai-only-model-catalog.test.ts index ef384ab5e..2b9cae55b 100644 --- a/src/cli/openai-only-model-catalog.test.ts +++ b/src/cli/openai-only-model-catalog.test.ts @@ -53,8 +53,8 @@ describe("generateModelConfig OpenAI-only model catalog", () => { const result = generateModelConfig(config) // #then - expect(result.agents?.explore).toEqual({ model: "opencode-go/minimax-m2.5" }) - expect(result.agents?.librarian).toEqual({ model: "opencode-go/minimax-m2.5" }) + expect(result.agents?.explore).toEqual({ model: "opencode-go/minimax-m2.7" }) + expect(result.agents?.librarian).toEqual({ model: "opencode-go/minimax-m2.7" }) expect(result.categories?.quick).toEqual({ model: "openai/gpt-5.4-mini" }) }) }) diff --git a/src/shared/model-requirements.test.ts b/src/shared/model-requirements.test.ts index d69de0ef5..d09a4530a 100644 --- a/src/shared/model-requirements.test.ts +++ b/src/shared/model-requirements.test.ts @@ -64,23 +64,23 @@ describe("AGENT_MODEL_REQUIREMENTS", () => { expect(last.model).toBe("big-pickle") }) - test("librarian has valid fallbackChain with opencode-go/minimax-m2.5 as primary", () => { + test("librarian has valid fallbackChain with opencode-go/minimax-m2.7 as primary", () => { // given - librarian agent requirement const librarian = AGENT_MODEL_REQUIREMENTS["librarian"] // when - accessing librarian requirement - // then - fallbackChain exists with opencode-go/minimax-m2.5 as first entry + // then - fallbackChain exists with opencode-go/minimax-m2.7 as first entry expect(librarian).toBeDefined() expect(librarian.fallbackChain).toBeArray() expect(librarian.fallbackChain.length).toBeGreaterThan(0) const primary = librarian.fallbackChain[0] expect(primary.providers[0]).toBe("opencode-go") - expect(primary.model).toBe("minimax-m2.5") + expect(primary.model).toBe("minimax-m2.7") const second = librarian.fallbackChain[1] expect(second.providers[0]).toBe("opencode") - expect(second.model).toBe("minimax-m2.5-free") + expect(second.model).toBe("minimax-m2.7-highspeed") const tertiary = librarian.fallbackChain[2] expect(tertiary.providers).toContain("anthropic") @@ -95,7 +95,7 @@ describe("AGENT_MODEL_REQUIREMENTS", () => { const explore = AGENT_MODEL_REQUIREMENTS["explore"] // when - accessing explore requirement - // then - fallbackChain: grok → opencode-go/minimax → minimax-free → haiku → nano + // then - fallbackChain: grok → minimax-m2.7-highspeed → minimax-m2.7 → haiku → nano expect(explore).toBeDefined() expect(explore.fallbackChain).toBeArray() expect(explore.fallbackChain).toHaveLength(5) @@ -106,11 +106,11 @@ describe("AGENT_MODEL_REQUIREMENTS", () => { const secondary = explore.fallbackChain[1] expect(secondary.providers).toContain("opencode-go") - expect(secondary.model).toBe("minimax-m2.5") + expect(secondary.model).toBe("minimax-m2.7-highspeed") const tertiary = explore.fallbackChain[2] expect(tertiary.providers).toContain("opencode") - expect(tertiary.model).toBe("minimax-m2.5-free") + expect(tertiary.model).toBe("minimax-m2.7") const quaternary = explore.fallbackChain[3] expect(quaternary.providers).toContain("anthropic") @@ -211,7 +211,7 @@ describe("AGENT_MODEL_REQUIREMENTS", () => { // then - fallbackChain exists with claude-sonnet-4-6 as first entry expect(atlas).toBeDefined() expect(atlas.fallbackChain).toBeArray() - expect(atlas.fallbackChain.length).toBeGreaterThan(0) + expect(atlas.fallbackChain).toHaveLength(4) const primary = atlas.fallbackChain[0] expect(primary.model).toBe("claude-sonnet-4-6") @@ -227,15 +227,20 @@ describe("AGENT_MODEL_REQUIREMENTS", () => { model: "gpt-5.4", variant: "medium", }) + + const quaternary = atlas.fallbackChain[3] + expect(quaternary.model).toBe("minimax-m2.7") + expect(quaternary.providers[0]).toBe("opencode-go") }) - test("sisyphus-junior has an OpenAI fallback before big-pickle", () => { + test("sisyphus-junior has an OpenAI fallback and minimax before big-pickle", () => { // given - sisyphus-junior agent requirement const sisyphusJunior = AGENT_MODEL_REQUIREMENTS["sisyphus-junior"] // when - locating the OpenAI fallback entry const openAiFallback = sisyphusJunior.fallbackChain.find((entry) => entry.providers.includes("openai")) const openAiFallbackIndex = sisyphusJunior.fallbackChain.findIndex((entry) => entry.providers.includes("openai")) + const minimaxIndex = sisyphusJunior.fallbackChain.findIndex((entry) => entry.model === "minimax-m2.7") const bigPickleIndex = sisyphusJunior.fallbackChain.findIndex((entry) => entry.model === "big-pickle") // then @@ -245,7 +250,8 @@ describe("AGENT_MODEL_REQUIREMENTS", () => { variant: "medium", }) expect(openAiFallbackIndex).toBeGreaterThan(-1) - expect(bigPickleIndex).toBeGreaterThan(openAiFallbackIndex) + expect(minimaxIndex).toBeGreaterThan(openAiFallbackIndex) + expect(bigPickleIndex).toBeGreaterThan(minimaxIndex) }) test("hephaestus supports openai, github-copilot, venice, and opencode providers", () => { @@ -437,10 +443,10 @@ describe("CATEGORY_MODEL_REQUIREMENTS", () => { const writing = CATEGORY_MODEL_REQUIREMENTS["writing"] // when - accessing writing requirement - // then - fallbackChain: gemini-3-flash -> kimi-k2.5 -> claude-sonnet-4-6 + // then - fallbackChain: gemini-3-flash -> kimi-k2.5 -> claude-sonnet-4-6 -> minimax-m2.7 expect(writing).toBeDefined() expect(writing.fallbackChain).toBeArray() - expect(writing.fallbackChain).toHaveLength(3) + expect(writing.fallbackChain).toHaveLength(4) const primary = writing.fallbackChain[0] expect(primary.model).toBe("gemini-3-flash") @@ -453,6 +459,10 @@ describe("CATEGORY_MODEL_REQUIREMENTS", () => { const third = writing.fallbackChain[2] expect(third.model).toBe("claude-sonnet-4-6") expect(third.providers[0]).toBe("anthropic") + + const fourth = writing.fallbackChain[3] + expect(fourth.model).toBe("minimax-m2.7") + expect(fourth.providers[0]).toBe("opencode-go") }) test("all 8 categories have valid fallbackChain arrays", () => { diff --git a/src/shared/model-requirements.ts b/src/shared/model-requirements.ts index 16f7e78c9..232e45444 100644 --- a/src/shared/model-requirements.ts +++ b/src/shared/model-requirements.ts @@ -72,8 +72,8 @@ export const AGENT_MODEL_REQUIREMENTS: Record = { }, librarian: { fallbackChain: [ - { providers: ["opencode-go"], model: "minimax-m2.5" }, - { providers: ["opencode"], model: "minimax-m2.5-free" }, + { providers: ["opencode-go"], model: "minimax-m2.7" }, + { providers: ["opencode"], model: "minimax-m2.7-highspeed" }, { providers: ["anthropic", "opencode"], model: "claude-haiku-4-5" }, { providers: ["opencode"], model: "gpt-5-nano" }, ], @@ -81,8 +81,8 @@ export const AGENT_MODEL_REQUIREMENTS: Record = { explore: { fallbackChain: [ { providers: ["github-copilot"], model: "grok-code-fast-1" }, - { providers: ["opencode-go"], model: "minimax-m2.5" }, - { providers: ["opencode"], model: "minimax-m2.5-free" }, + { providers: ["opencode-go"], model: "minimax-m2.7-highspeed" }, + { providers: ["opencode"], model: "minimax-m2.7" }, { providers: ["anthropic", "opencode"], model: "claude-haiku-4-5" }, { providers: ["opencode"], model: "gpt-5-nano" }, ], @@ -159,6 +159,7 @@ export const AGENT_MODEL_REQUIREMENTS: Record = { model: "gpt-5.4", variant: "medium", }, + { providers: ["opencode-go"], model: "minimax-m2.7" }, ], }, "sisyphus-junior": { @@ -170,6 +171,7 @@ export const AGENT_MODEL_REQUIREMENTS: Record = { model: "gpt-5.4", variant: "medium", }, + { providers: ["opencode-go"], model: "minimax-m2.7" }, { providers: ["opencode"], model: "big-pickle" }, ], }, @@ -263,7 +265,7 @@ export const CATEGORY_MODEL_REQUIREMENTS: Record = { providers: ["google", "github-copilot", "opencode"], model: "gemini-3-flash", }, - { providers: ["opencode-go"], model: "minimax-m2.5" }, + { providers: ["opencode-go"], model: "minimax-m2.7" }, { providers: ["opencode"], model: "gpt-5-nano" }, ], }, @@ -283,6 +285,7 @@ export const CATEGORY_MODEL_REQUIREMENTS: Record = { providers: ["google", "github-copilot", "opencode"], model: "gemini-3-flash", }, + { providers: ["opencode-go"], model: "minimax-m2.7" }, ], }, "unspecified-high": { @@ -325,6 +328,7 @@ export const CATEGORY_MODEL_REQUIREMENTS: Record = { providers: ["anthropic", "github-copilot", "opencode"], model: "claude-sonnet-4-6", }, + { providers: ["opencode-go"], model: "minimax-m2.7" }, ], }, }; From 62d270400928df32d145768915403ee8e0e15d1b Mon Sep 17 00:00:00 2001 From: MoerAI Date: Mon, 23 Mar 2026 10:34:22 +0900 Subject: [PATCH 05/63] fix(runtime-fallback): detect prettified quota errors without HTTP status codes (fixes #2747) --- src/hooks/runtime-fallback/constants.ts | 7 +- .../runtime-fallback/error-classifier.test.ts | 73 +++++++++++++++++++ .../runtime-fallback/error-classifier.ts | 23 ++++++ 3 files changed, 102 insertions(+), 1 deletion(-) diff --git a/src/hooks/runtime-fallback/constants.ts b/src/hooks/runtime-fallback/constants.ts index 3f011b333..3bd4409a4 100644 --- a/src/hooks/runtime-fallback/constants.ts +++ b/src/hooks/runtime-fallback/constants.ts @@ -11,7 +11,7 @@ import type { RuntimeFallbackConfig } from "../../config" */ export const DEFAULT_CONFIG: Required = { enabled: false, - retry_on_errors: [429, 500, 502, 503, 504], + retry_on_errors: [402, 429, 500, 502, 503, 504], max_fallback_attempts: 3, cooldown_seconds: 60, timeout_seconds: 30, @@ -37,6 +37,11 @@ export const RETRYABLE_ERROR_PATTERNS = [ /try.?again/i, /credit.*balance.*too.*low/i, /insufficient.?(?:credits?|funds?|balance)/i, + /subscription.*quota/i, + /billing.?(?:hard.?)?limit/i, + /payment.?required/i, + /out\s+of\s+credits?/i, + /(?:^|\s)402(?:\s|$)/, /(?:^|\s)429(?:\s|$)/, /(?:^|\s)503(?:\s|$)/, /(?:^|\s)529(?:\s|$)/, diff --git a/src/hooks/runtime-fallback/error-classifier.test.ts b/src/hooks/runtime-fallback/error-classifier.test.ts index 954e28758..20f1c0c7e 100644 --- a/src/hooks/runtime-fallback/error-classifier.test.ts +++ b/src/hooks/runtime-fallback/error-classifier.test.ts @@ -166,3 +166,76 @@ describe("extractStatusCode", () => { expect(extractStatusCode(error)).toBe(400) }) }) + +describe("quota error detection (fixes #2747)", () => { + test("classifies prettified subscription quota error as quota_exceeded", () => { + //#given + const error = { + name: "AI_APICallError", + message: "Subscription quota exceeded. You can continue using free models.", + } + + //#when + const errorType = classifyErrorType(error) + const retryable = isRetryableError(error, [402, 429, 500, 502, 503, 504]) + + //#then + expect(errorType).toBe("quota_exceeded") + expect(retryable).toBe(true) + }) + + test("classifies billing hard limit error as quota_exceeded", () => { + //#given + const error = { message: "You have reached your billing hard limit." } + + //#when + const errorType = classifyErrorType(error) + + //#then + expect(errorType).toBe("quota_exceeded") + }) + + test("classifies exhausted capacity error as quota_exceeded", () => { + //#given + const error = { message: "You have exhausted your capacity on this model." } + + //#when + const errorType = classifyErrorType(error) + + //#then + expect(errorType).toBe("quota_exceeded") + }) + + test("classifies out of credits error as quota_exceeded", () => { + //#given + const error = { message: "Out of credits. Please add more credits to continue." } + + //#when + const errorType = classifyErrorType(error) + + //#then + expect(errorType).toBe("quota_exceeded") + }) + + test("treats HTTP 402 Payment Required as retryable", () => { + //#given + const error = { statusCode: 402, message: "Payment Required" } + + //#when + const retryable = isRetryableError(error, [402, 429, 500, 502, 503, 504]) + + //#then + expect(retryable).toBe(true) + }) + + test("matches subscription quota pattern in RETRYABLE_ERROR_PATTERNS", () => { + //#given + const error = { message: "Subscription quota exceeded. You can continue using free models." } + + //#when + const retryable = isRetryableError(error, [429, 503]) + + //#then + expect(retryable).toBe(true) + }) +}) diff --git a/src/hooks/runtime-fallback/error-classifier.ts b/src/hooks/runtime-fallback/error-classifier.ts index e581f3fb8..12c70432e 100644 --- a/src/hooks/runtime-fallback/error-classifier.ts +++ b/src/hooks/runtime-fallback/error-classifier.ts @@ -21,6 +21,13 @@ export function getErrorMessage(error: unknown): string { } } + const errorObj2 = error as Record + const name = errorObj2.name + if (typeof name === "string" && name.length > 0) { + const nameColonMatch = name.match(/:\s*(.+)/) + if (nameColonMatch) return nameColonMatch[1].trim().toLowerCase() + } + try { return JSON.stringify(error).toLowerCase() } catch { @@ -112,6 +119,18 @@ export function classifyErrorType(error: unknown): string | undefined { return "model_not_found" } + if ( + /quota.?exceeded/i.test(message) || + /subscription.*quota/i.test(message) || + /insufficient.?quota/i.test(message) || + /billing.?(?:hard.?)?limit/i.test(message) || + /exhausted\s+your\s+capacity/i.test(message) || + /out\s+of\s+credits?/i.test(message) || + /payment.?required/i.test(message) + ) { + return "quota_exceeded" + } + return undefined } @@ -181,6 +200,10 @@ export function isRetryableError(error: unknown, retryOnErrors: number[]): boole return true } + if (errorType === "quota_exceeded") { + return true + } + if (statusCode && retryOnErrors.includes(statusCode)) { return true } From 29a7bc2d314cee2c2ed9c34605983aec79b85ea3 Mon Sep 17 00:00:00 2001 From: MoerAI Date: Mon, 23 Mar 2026 10:41:37 +0900 Subject: [PATCH 06/63] fix(plugin): display friendly name in configuration UI instead of file path (fixes #2644) --- src/index.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/index.ts b/src/index.ts index 70156f109..c97fedcd7 100644 --- a/src/index.ts +++ b/src/index.ts @@ -89,6 +89,7 @@ const OhMyOpenCodePlugin: Plugin = async (ctx) => { activePluginDispose = dispose return { + name: "oh-my-openagent", ...pluginInterface, "experimental.session.compacting": async ( From f16d55ad9503a322c096289f8d8089e6854100d0 Mon Sep 17 00:00:00 2001 From: MoerAI Date: Mon, 23 Mar 2026 15:19:09 +0900 Subject: [PATCH 07/63] fix: add errorName-based quota detection and strengthen test coverage --- .../runtime-fallback/error-classifier.test.ts | 24 +++++++++++++++++++ .../runtime-fallback/error-classifier.ts | 3 +++ 2 files changed, 27 insertions(+) diff --git a/src/hooks/runtime-fallback/error-classifier.test.ts b/src/hooks/runtime-fallback/error-classifier.test.ts index 20f1c0c7e..ba9b70f1a 100644 --- a/src/hooks/runtime-fallback/error-classifier.test.ts +++ b/src/hooks/runtime-fallback/error-classifier.test.ts @@ -238,4 +238,28 @@ describe("quota error detection (fixes #2747)", () => { //#then expect(retryable).toBe(true) }) + + test("classifies QuotaExceededError by errorName even without quota keywords in message", () => { + //#given + const error = { name: "QuotaExceededError", message: "Request failed." } + + //#when + const errorType = classifyErrorType(error) + + //#then + expect(errorType).toBe("quota_exceeded") + }) + + test("matches payment required pattern directly via RETRYABLE_ERROR_PATTERNS", () => { + //#given — message has no quota keyword, only "payment required" + const error = { message: "Error 402: payment required for this request" } + + //#when — classifyErrorType will NOT match (no quota keyword), so isRetryableError must use RETRYABLE_ERROR_PATTERNS + const errorType = classifyErrorType(error) + const retryable = isRetryableError(error, [429, 503]) + + //#then + expect(errorType).toBe("quota_exceeded") + expect(retryable).toBe(true) + }) }) diff --git a/src/hooks/runtime-fallback/error-classifier.ts b/src/hooks/runtime-fallback/error-classifier.ts index 12c70432e..088735edd 100644 --- a/src/hooks/runtime-fallback/error-classifier.ts +++ b/src/hooks/runtime-fallback/error-classifier.ts @@ -120,6 +120,9 @@ export function classifyErrorType(error: unknown): string | undefined { } if ( + errorName?.includes("quotaexceeded") || + errorName?.includes("insufficientquota") || + errorName?.includes("billingerror") || /quota.?exceeded/i.test(message) || /subscription.*quota/i.test(message) || /insufficient.?quota/i.test(message) || From bf804b062664e8d04824c46eb8fcc1cb6a0a3784 Mon Sep 17 00:00:00 2001 From: YeonGyu-Kim Date: Wed, 25 Mar 2026 14:29:59 +0900 Subject: [PATCH 08/63] fix(shared): restrict cached Anthropic 1M context to GA 4.6 models only --- ...indow-monitor.model-context-limits.test.ts | 137 ++++++++++++------ src/shared/context-limit-resolver.test.ts | 36 ++++- src/shared/context-limit-resolver.ts | 12 +- 3 files changed, 137 insertions(+), 48 deletions(-) diff --git a/src/hooks/context-window-monitor.model-context-limits.test.ts b/src/hooks/context-window-monitor.model-context-limits.test.ts index f9e2fe43e..919050120 100644 --- a/src/hooks/context-window-monitor.model-context-limits.test.ts +++ b/src/hooks/context-window-monitor.model-context-limits.test.ts @@ -135,9 +135,96 @@ describe("context-window-monitor modelContextLimitsCache", () => { }) }) - describe("#given Anthropic provider with cached context limit and 1M mode disabled", () => { + describe("#given Anthropic 4.6 provider with cached context limit and 1M mode disabled", () => { describe("#when cached usage is below threshold of cached limit", () => { it("#then should respect the cached limit and skip the reminder", async () => { + // given + const modelContextLimitsCache = new Map() + modelContextLimitsCache.set("anthropic/claude-sonnet-4-6", 500000) + + const hook = createContextWindowMonitorHook({} as never, { + anthropicContext1MEnabled: false, + modelContextLimitsCache, + }) + const sessionID = "ses_anthropic_cached_limit_respected" + + await hook.event({ + event: { + type: "message.updated", + properties: { + info: { + role: "assistant", + sessionID, + providerID: "anthropic", + modelID: "claude-sonnet-4-6", + finish: true, + tokens: { + input: 150000, + output: 0, + reasoning: 0, + cache: { read: 10000, write: 0 }, + }, + }, + }, + }, + }) + + // when + const output = createOutput() + await hook["tool.execute.after"]({ tool: "bash", sessionID, callID: "call_1" }, output) + + // then — 160K/500K = 32%, well below 70% threshold + expect(output.output).toBe("original") + }) + }) + + describe("#when cached usage exceeds threshold of cached limit", () => { + it("#then should use the cached limit for the reminder", async () => { + // given + const modelContextLimitsCache = new Map() + modelContextLimitsCache.set("anthropic/claude-sonnet-4-6", 500000) + + const hook = createContextWindowMonitorHook({} as never, { + anthropicContext1MEnabled: false, + modelContextLimitsCache, + }) + const sessionID = "ses_anthropic_cached_limit_exceeded" + + await hook.event({ + event: { + type: "message.updated", + properties: { + info: { + role: "assistant", + sessionID, + providerID: "anthropic", + modelID: "claude-sonnet-4-6", + finish: true, + tokens: { + input: 350000, + output: 0, + reasoning: 0, + cache: { read: 10000, write: 0 }, + }, + }, + }, + }, + }) + + // when + const output = createOutput() + await hook["tool.execute.after"]({ tool: "bash", sessionID, callID: "call_1" }, output) + + // then — 360K/500K = 72%, above 70% threshold, uses cached 500K limit + expect(output.output).toContain("context remaining") + expect(output.output).toContain("500,000-token context window") + }) + }) + }) + + describe("#given older Anthropic provider with cached context limit and 1M mode disabled", () => { + describe("#when cached usage would only exceed the incorrect cached limit", () => { + it("#then should ignore the cached limit and use the 200K default", async () => { // given const modelContextLimitsCache = new Map() modelContextLimitsCache.set("anthropic/claude-sonnet-4-5", 500000) @@ -146,7 +233,7 @@ describe("context-window-monitor modelContextLimitsCache", () => { anthropicContext1MEnabled: false, modelContextLimitsCache, }) - const sessionID = "ses_anthropic_cached_limit_respected" + const sessionID = "ses_anthropic_older_model_ignores_cached_limit" await hook.event({ event: { @@ -173,51 +260,9 @@ describe("context-window-monitor modelContextLimitsCache", () => { const output = createOutput() await hook["tool.execute.after"]({ tool: "bash", sessionID, callID: "call_1" }, output) - // then — 160K/500K = 32%, well below 70% threshold - expect(output.output).toBe("original") - }) - }) - - describe("#when cached usage exceeds threshold of cached limit", () => { - it("#then should use the cached limit for the reminder", async () => { - // given - const modelContextLimitsCache = new Map() - modelContextLimitsCache.set("anthropic/claude-sonnet-4-5", 500000) - - const hook = createContextWindowMonitorHook({} as never, { - anthropicContext1MEnabled: false, - modelContextLimitsCache, - }) - const sessionID = "ses_anthropic_cached_limit_exceeded" - - await hook.event({ - event: { - type: "message.updated", - properties: { - info: { - role: "assistant", - sessionID, - providerID: "anthropic", - modelID: "claude-sonnet-4-5", - finish: true, - tokens: { - input: 350000, - output: 0, - reasoning: 0, - cache: { read: 10000, write: 0 }, - }, - }, - }, - }, - }) - - // when - const output = createOutput() - await hook["tool.execute.after"]({ tool: "bash", sessionID, callID: "call_1" }, output) - - // then — 360K/500K = 72%, above 70% threshold, uses cached 500K limit + // then expect(output.output).toContain("context remaining") - expect(output.output).toContain("500,000-token context window") + expect(output.output).toContain("200,000-token context window") }) }) }) diff --git a/src/shared/context-limit-resolver.test.ts b/src/shared/context-limit-resolver.test.ts index f08dd1d18..749eef287 100644 --- a/src/shared/context-limit-resolver.test.ts +++ b/src/shared/context-limit-resolver.test.ts @@ -28,7 +28,7 @@ describe("resolveActualContextLimit", () => { resetContextLimitEnv() }) - it("returns cached limit for Anthropic models when 1M mode is disabled (GA support)", () => { + it("returns cached limit for Anthropic 4.6 models when 1M mode is disabled (GA support)", () => { // given delete process.env[ANTHROPIC_CONTEXT_ENV_KEY] delete process.env[VERTEX_CONTEXT_ENV_KEY] @@ -45,6 +45,23 @@ describe("resolveActualContextLimit", () => { expect(actualLimit).toBe(1_000_000) }) + it("returns default 200K for older Anthropic models even when cached limit is higher", () => { + // given + delete process.env[ANTHROPIC_CONTEXT_ENV_KEY] + delete process.env[VERTEX_CONTEXT_ENV_KEY] + const modelContextLimitsCache = new Map() + modelContextLimitsCache.set("anthropic/claude-sonnet-4-5", 500_000) + + // when + const actualLimit = resolveActualContextLimit("anthropic", "claude-sonnet-4-5", { + anthropicContext1MEnabled: false, + modelContextLimitsCache, + }) + + // then + expect(actualLimit).toBe(200_000) + }) + it("returns default 200K for Anthropic models without cached limit and 1M mode disabled", () => { // given delete process.env[ANTHROPIC_CONTEXT_ENV_KEY] @@ -92,6 +109,23 @@ describe("resolveActualContextLimit", () => { expect(actualLimit).toBe(200000) }) + it("supports Anthropic 4.6 dot-version model IDs without explicit 1M mode", () => { + // given + delete process.env[ANTHROPIC_CONTEXT_ENV_KEY] + delete process.env[VERTEX_CONTEXT_ENV_KEY] + const modelContextLimitsCache = new Map() + modelContextLimitsCache.set("anthropic/claude-opus-4.6", 1_000_000) + + // when + const actualLimit = resolveActualContextLimit("anthropic", "claude-opus-4.6", { + anthropicContext1MEnabled: false, + modelContextLimitsCache, + }) + + // then + expect(actualLimit).toBe(1_000_000) + }) + it("returns null for non-Anthropic providers without a cached limit", () => { // given delete process.env[ANTHROPIC_CONTEXT_ENV_KEY] diff --git a/src/shared/context-limit-resolver.ts b/src/shared/context-limit-resolver.ts index 448127125..bc19a4191 100644 --- a/src/shared/context-limit-resolver.ts +++ b/src/shared/context-limit-resolver.ts @@ -1,6 +1,12 @@ import process from "node:process" const DEFAULT_ANTHROPIC_ACTUAL_LIMIT = 200_000 +const ANTHROPIC_NO_HEADER_GA_MODEL_IDS = new Set([ + "claude-opus-4-6", + "claude-opus-4.6", + "claude-sonnet-4-6", + "claude-sonnet-4.6", +]) export type ContextLimitModelCacheState = { anthropicContext1MEnabled: boolean @@ -20,6 +26,10 @@ function getAnthropicActualLimit(modelCacheState?: ContextLimitModelCacheState): : DEFAULT_ANTHROPIC_ACTUAL_LIMIT } +function isAnthropicNoHeaderGaModel(modelID: string): boolean { + return ANTHROPIC_NO_HEADER_GA_MODEL_IDS.has(modelID.toLowerCase()) +} + export function resolveActualContextLimit( providerID: string, modelID: string, @@ -30,7 +40,7 @@ export function resolveActualContextLimit( if (explicit1M === 1_000_000) return explicit1M const cachedLimit = modelCacheState?.modelContextLimitsCache?.get(`${providerID}/${modelID}`) - if (cachedLimit) return cachedLimit + if (cachedLimit && isAnthropicNoHeaderGaModel(modelID)) return cachedLimit return DEFAULT_ANTHROPIC_ACTUAL_LIMIT } From 44fb1143707f29b608a0e902db25257fb6310d36 Mon Sep 17 00:00:00 2001 From: MoerAI Date: Wed, 25 Mar 2026 16:58:49 +0900 Subject: [PATCH 09/63] fix(runtime-fallback): rename misleading test to match actual behavior The test name claimed it exercised RETRYABLE_ERROR_PATTERNS directly, but classifyErrorType actually matches 'payment required' via the quota_exceeded path first. Rename to 'detects payment required errors as retryable' to accurately describe end-to-end behavior. --- src/hooks/runtime-fallback/error-classifier.test.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/hooks/runtime-fallback/error-classifier.test.ts b/src/hooks/runtime-fallback/error-classifier.test.ts index ba9b70f1a..63e38733e 100644 --- a/src/hooks/runtime-fallback/error-classifier.test.ts +++ b/src/hooks/runtime-fallback/error-classifier.test.ts @@ -250,11 +250,11 @@ describe("quota error detection (fixes #2747)", () => { expect(errorType).toBe("quota_exceeded") }) - test("matches payment required pattern directly via RETRYABLE_ERROR_PATTERNS", () => { - //#given — message has no quota keyword, only "payment required" + test("detects payment required errors as retryable", () => { + //#given const error = { message: "Error 402: payment required for this request" } - //#when — classifyErrorType will NOT match (no quota keyword), so isRetryableError must use RETRYABLE_ERROR_PATTERNS + //#when const errorType = classifyErrorType(error) const retryable = isRetryableError(error, [429, 503]) From d7a1945b270e0644e854b6e470ab9904c0202d3a Mon Sep 17 00:00:00 2001 From: MoerAI Date: Wed, 25 Mar 2026 17:10:07 +0900 Subject: [PATCH 10/63] fix(plugin-loader): preserve scoped npm package names in plugin key parsing Scoped packages like @scope/pkg were truncated to just 'pkg' because basename() strips the scope prefix. Fix: - Detect scoped packages (starting with @) and find version separator after the scope slash, not at the leading @ - Return full scoped name (@scope/pkg) instead of calling basename - Add regression test for scoped package name preservation --- .../discovery.test.ts | 35 +++++++++++++++++++ .../claude-code-plugin-loader/discovery.ts | 13 ++++++- 2 files changed, 47 insertions(+), 1 deletion(-) diff --git a/src/features/claude-code-plugin-loader/discovery.test.ts b/src/features/claude-code-plugin-loader/discovery.test.ts index dab25cd22..63e2340a6 100644 --- a/src/features/claude-code-plugin-loader/discovery.test.ts +++ b/src/features/claude-code-plugin-loader/discovery.test.ts @@ -32,6 +32,41 @@ describe("discoverInstalledPlugins", () => { } }) + it("preserves scoped package name from npm plugin keys", () => { + //#given + const pluginsHome = process.env.CLAUDE_PLUGINS_HOME as string + const installPath = join(createTemporaryDirectory("omo-plugin-install-"), "@myorg", "my-plugin") + mkdirSync(installPath, { recursive: true }) + + const databasePath = join(pluginsHome, "installed_plugins.json") + writeFileSync( + databasePath, + JSON.stringify({ + version: 2, + plugins: { + "@myorg/my-plugin@1.0.0": [ + { + scope: "user", + installPath, + version: "1.0.0", + installedAt: "2026-03-25T00:00:00Z", + lastUpdated: "2026-03-25T00:00:00Z", + }, + ], + }, + }), + "utf-8", + ) + + //#when + const discovered = discoverInstalledPlugins() + + //#then + expect(discovered.errors).toHaveLength(0) + expect(discovered.plugins).toHaveLength(1) + expect(discovered.plugins[0]?.name).toBe("@myorg/my-plugin") + }) + it("derives package name from file URL plugin keys", () => { //#given const pluginsHome = process.env.CLAUDE_PLUGINS_HOME as string diff --git a/src/features/claude-code-plugin-loader/discovery.ts b/src/features/claude-code-plugin-loader/discovery.ts index 217e7211e..d4d9bb577 100644 --- a/src/features/claude-code-plugin-loader/discovery.ts +++ b/src/features/claude-code-plugin-loader/discovery.ts @@ -81,7 +81,14 @@ function loadPluginManifest(installPath: string): PluginManifest | null { function derivePluginNameFromKey(pluginKey: string): string { const keyWithoutSource = pluginKey.startsWith("npm:") ? pluginKey.slice(4) : pluginKey - const versionSeparator = keyWithoutSource.lastIndexOf("@") + + let versionSeparator: number + if (keyWithoutSource.startsWith("@")) { + const scopeEnd = keyWithoutSource.indexOf("/") + versionSeparator = scopeEnd > 0 ? keyWithoutSource.indexOf("@", scopeEnd) : -1 + } else { + versionSeparator = keyWithoutSource.lastIndexOf("@") + } const keyWithoutVersion = versionSeparator > 0 ? keyWithoutSource.slice(0, versionSeparator) : keyWithoutSource if (keyWithoutVersion.startsWith("file://")) { @@ -92,6 +99,10 @@ function derivePluginNameFromKey(pluginKey: string): string { } } + if (keyWithoutVersion.startsWith("@") && keyWithoutVersion.includes("/")) { + return keyWithoutVersion + } + if (keyWithoutVersion.includes("/") || keyWithoutVersion.includes("\\")) { return basename(keyWithoutVersion) } From 2af9324400a942e9915f228c8e3a72368a1f9bcd Mon Sep 17 00:00:00 2001 From: Ravi Tharuma Date: Wed, 25 Mar 2026 14:47:46 +0100 Subject: [PATCH 11/63] feat: add models.dev-backed model capabilities --- assets/oh-my-opencode.schema.json | 21 + package.json | 1 + script/build-model-capabilities.ts | 13 + src/cli/cli-program.ts | 16 + src/cli/refresh-model-capabilities.test.ts | 114 + src/cli/refresh-model-capabilities.ts | 51 + src/config/index.ts | 1 + src/config/schema.test.ts | 31 + src/config/schema.ts | 1 + src/config/schema/model-capabilities.ts | 10 + src/config/schema/oh-my-opencode-config.ts | 2 + .../model-capabilities.generated.json | 40690 ++++++++++++++++ src/hooks/auto-update-checker/hook.test.ts | 12 + src/hooks/auto-update-checker/hook.ts | 9 +- .../hook/model-capabilities-status.ts | 37 + src/hooks/auto-update-checker/types.ts | 3 + src/plugin/chat-params.test.ts | 83 +- src/plugin/chat-params.ts | 92 +- src/plugin/hooks/create-session-hooks.ts | 1 + src/shared/connected-providers-cache.test.ts | 59 +- src/shared/connected-providers-cache.ts | 72 +- src/shared/index.ts | 3 + src/shared/model-capabilities-cache.test.ts | 134 + src/shared/model-capabilities-cache.ts | 241 + src/shared/model-capabilities.test.ts | 159 + src/shared/model-capabilities.ts | 228 + src/shared/model-capability-heuristics.ts | 93 + .../model-settings-compatibility.test.ts | 57 + src/shared/model-settings-compatibility.ts | 144 +- 29 files changed, 42264 insertions(+), 114 deletions(-) create mode 100644 script/build-model-capabilities.ts create mode 100644 src/cli/refresh-model-capabilities.test.ts create mode 100644 src/cli/refresh-model-capabilities.ts create mode 100644 src/config/schema/model-capabilities.ts create mode 100644 src/generated/model-capabilities.generated.json create mode 100644 src/hooks/auto-update-checker/hook/model-capabilities-status.ts create mode 100644 src/shared/model-capabilities-cache.test.ts create mode 100644 src/shared/model-capabilities-cache.ts create mode 100644 src/shared/model-capabilities.test.ts create mode 100644 src/shared/model-capabilities.ts create mode 100644 src/shared/model-capability-heuristics.ts diff --git a/assets/oh-my-opencode.schema.json b/assets/oh-my-opencode.schema.json index 4324e186c..3ab34f714 100644 --- a/assets/oh-my-opencode.schema.json +++ b/assets/oh-my-opencode.schema.json @@ -4696,6 +4696,27 @@ }, "additionalProperties": false }, + "model_capabilities": { + "type": "object", + "properties": { + "enabled": { + "type": "boolean" + }, + "auto_refresh_on_start": { + "type": "boolean" + }, + "refresh_timeout_ms": { + "type": "integer", + "exclusiveMinimum": 0, + "maximum": 9007199254740991 + }, + "source_url": { + "type": "string", + "format": "uri" + } + }, + "additionalProperties": false + }, "openclaw": { "type": "object", "properties": { diff --git a/package.json b/package.json index 952fdbcfc..1b496f000 100644 --- a/package.json +++ b/package.json @@ -25,6 +25,7 @@ "build:all": "bun run build && bun run build:binaries", "build:binaries": "bun run script/build-binaries.ts", "build:schema": "bun run script/build-schema.ts", + "build:model-capabilities": "bun run script/build-model-capabilities.ts", "clean": "rm -rf dist", "prepare": "bun run build", "postinstall": "node postinstall.mjs", diff --git a/script/build-model-capabilities.ts b/script/build-model-capabilities.ts new file mode 100644 index 000000000..64f1d84f2 --- /dev/null +++ b/script/build-model-capabilities.ts @@ -0,0 +1,13 @@ +import { writeFileSync } from "fs" +import { resolve } from "path" +import { + fetchModelCapabilitiesSnapshot, + MODELS_DEV_SOURCE_URL, +} from "../src/shared/model-capabilities-cache" + +const OUTPUT_PATH = resolve(import.meta.dir, "../src/generated/model-capabilities.generated.json") + +console.log(`Fetching model capabilities snapshot from ${MODELS_DEV_SOURCE_URL}...`) +const snapshot = await fetchModelCapabilitiesSnapshot() +writeFileSync(OUTPUT_PATH, `${JSON.stringify(snapshot, null, 2)}\n`) +console.log(`Generated ${OUTPUT_PATH} with ${Object.keys(snapshot.models).length} models`) diff --git a/src/cli/cli-program.ts b/src/cli/cli-program.ts index ddab0483c..15d3d0489 100644 --- a/src/cli/cli-program.ts +++ b/src/cli/cli-program.ts @@ -3,6 +3,7 @@ import { install } from "./install" import { run } from "./run" import { getLocalVersion } from "./get-local-version" import { doctor } from "./doctor" +import { refreshModelCapabilities } from "./refresh-model-capabilities" import { createMcpOAuthCommand } from "./mcp-oauth" import type { InstallArgs } from "./types" import type { RunOptions } from "./run" @@ -176,6 +177,21 @@ Examples: process.exit(exitCode) }) +program + .command("refresh-model-capabilities") + .description("Refresh the cached models.dev-based model capabilities snapshot") + .option("-d, --directory ", "Working directory to read oh-my-opencode config from") + .option("--source-url ", "Override the models.dev source URL") + .option("--json", "Output refresh summary as JSON") + .action(async (options) => { + const exitCode = await refreshModelCapabilities({ + directory: options.directory, + sourceUrl: options.sourceUrl, + json: options.json ?? false, + }) + process.exit(exitCode) + }) + program .command("version") .description("Show version information") diff --git a/src/cli/refresh-model-capabilities.test.ts b/src/cli/refresh-model-capabilities.test.ts new file mode 100644 index 000000000..800cf7e54 --- /dev/null +++ b/src/cli/refresh-model-capabilities.test.ts @@ -0,0 +1,114 @@ +import { describe, expect, it, mock } from "bun:test" + +import { refreshModelCapabilities } from "./refresh-model-capabilities" + +describe("refreshModelCapabilities", () => { + it("uses config source_url when CLI override is absent", async () => { + const loadConfig = mock(() => ({ + model_capabilities: { + source_url: "https://mirror.example/api.json", + }, + })) + const refreshCache = mock(async () => ({ + generatedAt: "2026-03-25T00:00:00.000Z", + sourceUrl: "https://mirror.example/api.json", + models: { + "gpt-5.4": { id: "gpt-5.4" }, + }, + })) + let stdout = "" + + const exitCode = await refreshModelCapabilities( + { directory: "/repo", json: false }, + { + loadConfig, + refreshCache, + stdout: { + write: (chunk: string) => { + stdout += chunk + return true + }, + } as never, + stderr: { + write: () => true, + } as never, + }, + ) + + expect(exitCode).toBe(0) + expect(loadConfig).toHaveBeenCalledWith("/repo", null) + expect(refreshCache).toHaveBeenCalledWith({ + sourceUrl: "https://mirror.example/api.json", + }) + expect(stdout).toContain("Refreshed model capabilities cache (1 models)") + }) + + it("CLI sourceUrl overrides config and supports json output", async () => { + const refreshCache = mock(async () => ({ + generatedAt: "2026-03-25T00:00:00.000Z", + sourceUrl: "https://override.example/api.json", + models: { + "gpt-5.4": { id: "gpt-5.4" }, + "claude-opus-4-6": { id: "claude-opus-4-6" }, + }, + })) + let stdout = "" + + const exitCode = await refreshModelCapabilities( + { + directory: "/repo", + json: true, + sourceUrl: "https://override.example/api.json", + }, + { + loadConfig: () => ({}), + refreshCache, + stdout: { + write: (chunk: string) => { + stdout += chunk + return true + }, + } as never, + stderr: { + write: () => true, + } as never, + }, + ) + + expect(exitCode).toBe(0) + expect(refreshCache).toHaveBeenCalledWith({ + sourceUrl: "https://override.example/api.json", + }) + expect(JSON.parse(stdout)).toEqual({ + sourceUrl: "https://override.example/api.json", + generatedAt: "2026-03-25T00:00:00.000Z", + modelCount: 2, + }) + }) + + it("returns exit code 1 when refresh fails", async () => { + let stderr = "" + + const exitCode = await refreshModelCapabilities( + { directory: "/repo" }, + { + loadConfig: () => ({}), + refreshCache: async () => { + throw new Error("boom") + }, + stdout: { + write: () => true, + } as never, + stderr: { + write: (chunk: string) => { + stderr += chunk + return true + }, + } as never, + }, + ) + + expect(exitCode).toBe(1) + expect(stderr).toContain("Failed to refresh model capabilities cache") + }) +}) diff --git a/src/cli/refresh-model-capabilities.ts b/src/cli/refresh-model-capabilities.ts new file mode 100644 index 000000000..fc9ff1282 --- /dev/null +++ b/src/cli/refresh-model-capabilities.ts @@ -0,0 +1,51 @@ +import { loadPluginConfig } from "../plugin-config" +import { refreshModelCapabilitiesCache } from "../shared/model-capabilities-cache" + +export type RefreshModelCapabilitiesOptions = { + directory?: string + json?: boolean + sourceUrl?: string +} + +type RefreshModelCapabilitiesDeps = { + loadConfig?: typeof loadPluginConfig + refreshCache?: typeof refreshModelCapabilitiesCache + stdout?: Pick + stderr?: Pick +} + +export async function refreshModelCapabilities( + options: RefreshModelCapabilitiesOptions, + deps: RefreshModelCapabilitiesDeps = {}, +): Promise { + const directory = options.directory ?? process.cwd() + const loadConfig = deps.loadConfig ?? loadPluginConfig + const refreshCache = deps.refreshCache ?? refreshModelCapabilitiesCache + const stdout = deps.stdout ?? process.stdout + const stderr = deps.stderr ?? process.stderr + + try { + const config = loadConfig(directory, null) + const sourceUrl = options.sourceUrl ?? config.model_capabilities?.source_url + const snapshot = await refreshCache({ sourceUrl }) + + const summary = { + sourceUrl: snapshot.sourceUrl, + generatedAt: snapshot.generatedAt, + modelCount: Object.keys(snapshot.models).length, + } + + if (options.json) { + stdout.write(`${JSON.stringify(summary, null, 2)}\n`) + } else { + stdout.write( + `Refreshed model capabilities cache (${summary.modelCount} models) from ${summary.sourceUrl}\n`, + ) + } + + return 0 + } catch (error) { + stderr.write(`Failed to refresh model capabilities cache: ${String(error)}\n`) + return 1 + } +} diff --git a/src/config/index.ts b/src/config/index.ts index 2f7f98578..57a347d3a 100644 --- a/src/config/index.ts +++ b/src/config/index.ts @@ -19,5 +19,6 @@ export type { SisyphusConfig, SisyphusTasksConfig, RuntimeFallbackConfig, + ModelCapabilitiesConfig, FallbackModels, } from "./schema" diff --git a/src/config/schema.test.ts b/src/config/schema.test.ts index acc45e0bd..7f4ad615f 100644 --- a/src/config/schema.test.ts +++ b/src/config/schema.test.ts @@ -147,6 +147,37 @@ describe("disabled_mcps schema", () => { }) }) +describe("OhMyOpenCodeConfigSchema - model_capabilities", () => { + test("accepts valid model capabilities config", () => { + const input = { + model_capabilities: { + enabled: true, + auto_refresh_on_start: true, + refresh_timeout_ms: 5000, + source_url: "https://models.dev/api.json", + }, + } + + const result = OhMyOpenCodeConfigSchema.safeParse(input) + + expect(result.success).toBe(true) + if (result.success) { + expect(result.data.model_capabilities).toEqual(input.model_capabilities) + } + }) + + test("rejects invalid model capabilities config", () => { + const result = OhMyOpenCodeConfigSchema.safeParse({ + model_capabilities: { + refresh_timeout_ms: -1, + source_url: "not-a-url", + }, + }) + + expect(result.success).toBe(false) + }) +}) + describe("AgentOverrideConfigSchema", () => { describe("category field", () => { test("accepts category as optional string", () => { diff --git a/src/config/schema.ts b/src/config/schema.ts index bcb36a175..04dd0b15b 100644 --- a/src/config/schema.ts +++ b/src/config/schema.ts @@ -13,6 +13,7 @@ export * from "./schema/fallback-models" export * from "./schema/git-env-prefix" export * from "./schema/git-master" export * from "./schema/hooks" +export * from "./schema/model-capabilities" export * from "./schema/notification" export * from "./schema/oh-my-opencode-config" export * from "./schema/ralph-loop" diff --git a/src/config/schema/model-capabilities.ts b/src/config/schema/model-capabilities.ts new file mode 100644 index 000000000..76adc6522 --- /dev/null +++ b/src/config/schema/model-capabilities.ts @@ -0,0 +1,10 @@ +import { z } from "zod" + +export const ModelCapabilitiesConfigSchema = z.object({ + enabled: z.boolean().optional(), + auto_refresh_on_start: z.boolean().optional(), + refresh_timeout_ms: z.number().int().positive().optional(), + source_url: z.string().url().optional(), +}) + +export type ModelCapabilitiesConfig = z.infer diff --git a/src/config/schema/oh-my-opencode-config.ts b/src/config/schema/oh-my-opencode-config.ts index ea7e479c5..434bfe7ee 100644 --- a/src/config/schema/oh-my-opencode-config.ts +++ b/src/config/schema/oh-my-opencode-config.ts @@ -13,6 +13,7 @@ import { ExperimentalConfigSchema } from "./experimental" import { GitMasterConfigSchema } from "./git-master" import { NotificationConfigSchema } from "./notification" import { OpenClawConfigSchema } from "./openclaw" +import { ModelCapabilitiesConfigSchema } from "./model-capabilities" import { RalphLoopConfigSchema } from "./ralph-loop" import { RuntimeFallbackConfigSchema } from "./runtime-fallback" import { SkillsConfigSchema } from "./skills" @@ -56,6 +57,7 @@ export const OhMyOpenCodeConfigSchema = z.object({ runtime_fallback: z.union([z.boolean(), RuntimeFallbackConfigSchema]).optional(), background_task: BackgroundTaskConfigSchema.optional(), notification: NotificationConfigSchema.optional(), + model_capabilities: ModelCapabilitiesConfigSchema.optional(), openclaw: OpenClawConfigSchema.optional(), babysitting: BabysittingConfigSchema.optional(), git_master: GitMasterConfigSchema.optional(), diff --git a/src/generated/model-capabilities.generated.json b/src/generated/model-capabilities.generated.json new file mode 100644 index 000000000..91b952581 --- /dev/null +++ b/src/generated/model-capabilities.generated.json @@ -0,0 +1,40690 @@ +{ + "generatedAt": "2026-03-25T13:44:08.677Z", + "sourceUrl": "https://models.dev/api.json", + "models": { + "nvidia/llama-3.3-70b-instruct-fp8": { + "id": "nvidia/Llama-3.3-70B-Instruct-FP8", + "family": "llama", + "reasoning": false, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 131072, + "output": 32768 + } + }, + "microsoft/phi-4-multimodal-instruct": { + "id": "microsoft/phi-4-multimodal-instruct", + "family": "phi", + "reasoning": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image", + "audio" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 4096 + }, + "temperature": true + }, + "intfloat/multilingual-e5-large-instruct": { + "id": "intfloat/multilingual-e5-large-instruct", + "family": "text-embedding", + "reasoning": false, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 512, + "output": 1024 + }, + "temperature": false + }, + "moonshotai/kimi-k2.5": { + "id": "moonshotai/kimi-k2.5", + "family": "kimi", + "reasoning": false, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 256000, + "output": 65536, + "input": 256000 + }, + "temperature": true + }, + "kblab/kb-whisper-large": { + "id": "KBLab/kb-whisper-large", + "family": "whisper", + "reasoning": false, + "toolCall": false, + "modalities": { + "input": [ + "audio" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 480000, + "output": 4800 + }, + "temperature": false + }, + "qwen/qwen3-30b-a3b-instruct-2507-fp8": { + "id": "Qwen/Qwen3-30B-A3B-Instruct-2507-FP8", + "family": "qwen", + "reasoning": false, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 64000, + "output": 64000 + } + }, + "qwen/qwen3-embedding-8b": { + "id": "Qwen/Qwen3-Embedding-8B", + "family": "qwen", + "reasoning": false, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 32000, + "output": 4096, + "input": 32768 + }, + "temperature": false + }, + "qwen/qwen3-vl-30b-a3b-instruct": { + "id": "qwen/qwen3-vl-30b-a3b-instruct", + "family": "qwen", + "reasoning": false, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 131072, + "output": 32768 + }, + "temperature": true + }, + "mistralai/voxtral-small-24b-2507": { + "id": "mistralai/voxtral-small-24b-2507", + "family": "voxtral", + "reasoning": false, + "toolCall": true, + "modalities": { + "input": [ + "text", + "audio" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 32000, + "output": 6400 + }, + "temperature": true + }, + "mistralai/devstral-small-2-24b-instruct-2512": { + "id": "mistralai/devstral-small-2-24b-instruct-2512", + "family": "devstral", + "reasoning": false, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 32768, + "output": 32768 + } + }, + "mistralai/magistral-small-2509": { + "id": "mistralai/Magistral-Small-2509", + "family": "magistral-small", + "reasoning": false, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 131072, + "output": 131072 + } + }, + "openai/gpt-oss-120b": { + "id": "openai/gpt-oss-120b", + "family": "gpt-oss", + "reasoning": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 16384, + "input": 128000 + }, + "temperature": true + }, + "openai/whisper-large-v3": { + "id": "openai/whisper-large-v3", + "family": "whisper", + "reasoning": false, + "toolCall": false, + "modalities": { + "input": [ + "audio" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 0, + "output": 4096 + }, + "temperature": false + }, + "glm-5": { + "id": "glm-5", + "family": "glm", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 202752, + "output": 16384 + } + }, + "glm-4.5-air": { + "id": "glm-4.5-air", + "family": "glm-air", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 131072, + "output": 131072 + } + }, + "glm-4.5": { + "id": "glm-4.5", + "family": "glm", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 131072, + "output": 131072 + } + }, + "glm-4.5-flash": { + "id": "glm-4.5-flash", + "family": "glm-flash", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 131072, + "output": 98304 + } + }, + "glm-4.7-flash": { + "id": "glm-4.7-flash", + "family": "glm", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 203000, + "output": 203000 + } + }, + "glm-4.6": { + "id": "glm-4.6", + "family": "glm", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 204800, + "output": 131072 + } + }, + "glm-4.7": { + "id": "glm-4.7", + "family": "glm", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 198000, + "output": 198000 + } + }, + "glm-5-turbo": { + "id": "glm-5-turbo", + "family": "glm", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 200000, + "output": 131072 + } + }, + "glm-4.5v": { + "id": "glm-4.5v", + "family": "glm", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image", + "video" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 64000, + "output": 16384 + } + }, + "glm-4.6v": { + "id": "glm-4.6v", + "family": "glm", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image", + "video" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 32768 + } + }, + "minimax-m2.5": { + "id": "MiniMax-M2.5", + "family": "minimax", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 204800, + "input": 196601, + "output": 131072 + } + }, + "qwen3-coder-next": { + "id": "qwen3-coder-next", + "family": "qwen", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 262144, + "output": 65536, + "input": 262144 + } + }, + "kimi-k2.5": { + "id": "kimi-k2.5", + "family": "kimi", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image", + "video" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 262144, + "output": 32768 + } + }, + "qwen3-max-2026-01-23": { + "id": "qwen3-max-2026-01-23", + "family": "qwen", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 256000, + "output": 32768, + "input": 256000 + } + }, + "qwen3.5-plus": { + "id": "qwen3.5-plus", + "family": "qwen", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image", + "video" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 1000000, + "output": 65536 + } + }, + "qwen3-coder-plus": { + "id": "qwen3-coder-plus", + "family": "qwen", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 1048576, + "output": 65536 + } + }, + "xiaomi/mimo-v2-omni": { + "id": "xiaomi/mimo-v2-omni", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image", + "video", + "audio" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 262144, + "output": 65536 + }, + "family": "mimo" + }, + "xiaomi/mimo-v2-flash-free": { + "id": "xiaomi/mimo-v2-flash-free", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 262000, + "output": 64000 + } + }, + "xiaomi/mimo-v2-flash": { + "id": "xiaomi/mimo-v2-flash", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 256000, + "output": 32768, + "input": 256000 + }, + "family": "mimo" + }, + "xiaomi/mimo-v2-pro": { + "id": "xiaomi/mimo-v2-pro", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 1000000, + "output": 128000 + }, + "family": "mimo" + }, + "kuaishou/kat-coder-pro-v1-free": { + "id": "kuaishou/kat-coder-pro-v1-free", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 256000, + "output": 64000 + } + }, + "kuaishou/kat-coder-pro-v1": { + "id": "kuaishou/kat-coder-pro-v1", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 256000, + "output": 64000 + } + }, + "stepfun/step-3.5-flash-free": { + "id": "stepfun/step-3.5-flash-free", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 256000, + "output": 64000 + } + }, + "stepfun/step-3.5-flash": { + "id": "stepfun/step-3.5-flash", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 256000, + "output": 256000 + }, + "family": "step" + }, + "stepfun/step-3": { + "id": "stepfun/step-3", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "image", + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 65536, + "output": 64000 + } + }, + "inclusionai/ling-1t": { + "id": "inclusionai/ling-1t", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 64000 + } + }, + "inclusionai/ring-1t": { + "id": "inclusionai/ring-1t", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 64000 + } + }, + "volcengine/doubao-seed-1.8": { + "id": "volcengine/doubao-seed-1.8", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image", + "video" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 256000, + "output": 64000 + } + }, + "volcengine/doubao-seed-2.0-pro": { + "id": "volcengine/doubao-seed-2.0-pro", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image", + "video" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 256000, + "output": 64000 + } + }, + "volcengine/doubao-seed-2.0-mini": { + "id": "volcengine/doubao-seed-2.0-mini", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image", + "video" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 256000, + "output": 64000 + } + }, + "volcengine/doubao-seed-code": { + "id": "volcengine/doubao-seed-code", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 256000, + "output": 64000 + } + }, + "volcengine/doubao-seed-2.0-lite": { + "id": "volcengine/doubao-seed-2.0-lite", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image", + "video" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 256000, + "output": 64000 + } + }, + "volcengine/doubao-seed-2.0-code": { + "id": "volcengine/doubao-seed-2.0-code", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 256000, + "output": 32000 + } + }, + "deepseek/deepseek-v3.2": { + "id": "deepseek/deepseek-v3.2", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "pdf" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 163000, + "output": 65536, + "input": 163000 + }, + "family": "deepseek" + }, + "deepseek/deepseek-chat": { + "id": "deepseek/deepseek-chat", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 163840, + "output": 163840 + } + }, + "deepseek/deepseek-v3.2-exp": { + "id": "deepseek/deepseek-v3.2-exp", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 163840, + "output": 65536 + }, + "family": "deepseek" + }, + "moonshotai/kimi-k2-0905": { + "id": "moonshotai/kimi-k2-0905", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 131072, + "output": 26215 + }, + "family": "kimi" + }, + "moonshotai/kimi-k2-thinking": { + "id": "moonshotai/kimi-k2-thinking", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 256000, + "output": 262144, + "input": 256000 + }, + "family": "kimi-thinking" + }, + "moonshotai/kimi-k2-thinking-turbo": { + "id": "moonshotai/kimi-k2-thinking-turbo", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 262114, + "output": 262114 + }, + "family": "kimi-thinking" + }, + "baidu/ernie-5.0-thinking-preview": { + "id": "baidu/ernie-5.0-thinking-preview", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image", + "video" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 64000 + } + }, + "google/gemini-2.5-flash": { + "id": "google/gemini-2.5-flash", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "audio", + "image", + "pdf", + "text", + "video" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 1048576, + "output": 65535 + }, + "family": "gemini-flash" + }, + "google/gemini-3.1-flash-lite-preview": { + "id": "google/gemini-3.1-flash-lite-preview", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "audio", + "image", + "pdf", + "text", + "video" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 1048576, + "output": 65536 + }, + "family": "gemini" + }, + "google/gemini-3-flash-preview": { + "id": "google/gemini-3-flash-preview", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 1048756, + "output": 65536, + "input": 1048756 + }, + "family": "gemini-flash" + }, + "google/gemini-2.5-flash-lite": { + "id": "google/gemini-2.5-flash-lite", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "audio", + "image", + "pdf", + "text", + "video" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 1048576, + "output": 65535 + }, + "family": "gemini-flash-lite" + }, + "google/gemini-3.1-pro-preview": { + "id": "google/gemini-3.1-pro-preview", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "audio", + "image", + "pdf", + "text", + "video" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 1048576, + "output": 65536 + }, + "family": "gemini" + }, + "google/gemini-3-pro-image-preview": { + "id": "google/gemini-3-pro-image-preview", + "reasoning": true, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "image", + "text" + ], + "output": [ + "image", + "text" + ] + }, + "limit": { + "context": 65536, + "output": 32768 + } + }, + "google/gemini-3-pro-preview": { + "id": "google/gemini-3-pro-preview", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "audio", + "image", + "pdf", + "text", + "video" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 1048576, + "output": 65536 + }, + "family": "gemini-pro" + }, + "google/gemini-2.5-pro": { + "id": "google/gemini-2.5-pro", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "audio", + "image", + "pdf", + "text", + "video" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 1048576, + "output": 65536 + }, + "family": "gemini-pro" + }, + "z-ai/glm-5": { + "id": "z-ai/glm-5", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 202752, + "output": 131072 + }, + "family": "glm" + }, + "z-ai/glm-4.7-flashx": { + "id": "z-ai/glm-4.7-flashx", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 200000, + "output": 64000 + } + }, + "z-ai/glm-4.5-air": { + "id": "z-ai/glm-4.5-air", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 131072, + "output": 98304 + }, + "family": "glm-air" + }, + "z-ai/glm-4.5": { + "id": "z-ai/glm-4.5", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 131072, + "output": 98304 + }, + "family": "glm" + }, + "z-ai/glm-4.6v-flash-free": { + "id": "z-ai/glm-4.6v-flash-free", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image", + "video" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 200000, + "output": 64000 + } + }, + "z-ai/glm-4.6": { + "id": "z-ai/glm-4.6", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 200000, + "output": 65535, + "input": 200000 + }, + "family": "glm" + }, + "z-ai/glm-4.7": { + "id": "z-ai/glm-4.7", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 202752, + "output": 65535 + }, + "family": "glm" + }, + "z-ai/glm-4.7-flash-free": { + "id": "z-ai/glm-4.7-flash-free", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 200000, + "output": 64000 + } + }, + "z-ai/glm-4.6v-flash": { + "id": "z-ai/glm-4.6v-flash", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image", + "video" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 200000, + "output": 64000 + } + }, + "z-ai/glm-5-turbo": { + "id": "z-ai/glm-5-turbo", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 200000, + "output": 128000 + } + }, + "z-ai/glm-4.6v": { + "id": "z-ai/glm-4.6v", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "image", + "text", + "video" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 131072, + "output": 131072 + } + }, + "qwen/qwen3.5-flash": { + "id": "qwen/qwen3.5-flash", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 1020000, + "output": 1020000 + } + }, + "qwen/qwen3.5-plus": { + "id": "Qwen/Qwen3.5-Plus", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image", + "video" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 1000000, + "output": 65536 + }, + "family": "qwen" + }, + "qwen/qwen3-max": { + "id": "qwen/qwen3-max", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 262144, + "output": 32768 + }, + "family": "qwen" + }, + "qwen/qwen3-coder-plus": { + "id": "qwen/qwen3-coder-plus", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 1000000, + "output": 65536 + } + }, + "x-ai/grok-code-fast-1": { + "id": "x-ai/grok-code-fast-1", + "reasoning": true, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 256000, + "output": 131072, + "input": 256000 + }, + "family": "grok" + }, + "x-ai/grok-4-fast": { + "id": "x-ai/grok-4-fast", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 2000000, + "output": 131072, + "input": 2000000 + }, + "family": "grok" + }, + "x-ai/grok-4": { + "id": "x-ai/grok-4", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "image", + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 256000, + "output": 51200 + }, + "family": "grok" + }, + "x-ai/grok-4.1-fast-non-reasoning": { + "id": "x-ai/grok-4.1-fast-non-reasoning", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image", + "audio", + "video" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 2000000, + "output": 2000000 + } + }, + "x-ai/grok-4.1-fast": { + "id": "x-ai/grok-4.1-fast", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 2000000, + "output": 131072, + "input": 2000000 + }, + "family": "grok" + }, + "x-ai/grok-4.2-fast": { + "id": "x-ai/grok-4.2-fast", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image", + "video" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 2000000, + "output": 30000 + } + }, + "x-ai/grok-4.2-fast-non-reasoning": { + "id": "x-ai/grok-4.2-fast-non-reasoning", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image", + "video" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 2000000, + "output": 30000 + } + }, + "openai/gpt-5.3-codex": { + "id": "openai/gpt-5.3-codex", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "image", + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 400000, + "output": 128000, + "input": 272000 + }, + "family": "gpt" + }, + "openai/gpt-5-codex": { + "id": "openai/gpt-5-codex", + "reasoning": false, + "temperature": false, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 256000, + "output": 32768, + "input": 256000 + }, + "family": "gpt-codex" + }, + "openai/gpt-5.2-codex": { + "id": "openai/gpt-5.2-codex", + "reasoning": true, + "temperature": false, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image", + "pdf" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 400000, + "output": 128000, + "input": 400000 + }, + "family": "gpt-codex" + }, + "openai/gpt-5.1": { + "id": "openai/gpt-5.1", + "reasoning": true, + "temperature": false, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image", + "pdf" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 400000, + "output": 128000, + "input": 400000 + }, + "family": "gpt" + }, + "openai/gpt-5.1-chat": { + "id": "openai/gpt-5.1-chat", + "reasoning": true, + "temperature": false, + "toolCall": false, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 400000, + "output": 128000, + "input": 400000 + }, + "family": "gpt" + }, + "openai/gpt-5.1-codex-mini": { + "id": "openai/gpt-5.1-codex-mini", + "reasoning": true, + "temperature": false, + "toolCall": false, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 400000, + "output": 128000, + "input": 400000 + }, + "family": "gpt-codex-mini" + }, + "openai/gpt-5.2": { + "id": "openai/gpt-5.2", + "reasoning": true, + "temperature": false, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image", + "pdf" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 400000, + "output": 128000, + "input": 400000 + }, + "family": "gpt" + }, + "openai/gpt-5": { + "id": "openai/gpt-5", + "reasoning": true, + "temperature": false, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image", + "pdf" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 400000, + "output": 128000, + "input": 400000 + }, + "family": "gpt" + }, + "openai/gpt-5.4": { + "id": "openai/gpt-5.4", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "image", + "pdf", + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 1050000, + "output": 128000, + "input": 922000 + }, + "family": "gpt" + }, + "openai/gpt-5.4-pro": { + "id": "openai/gpt-5.4-pro", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "image", + "pdf", + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 1050000, + "output": 128000, + "input": 922000 + }, + "family": "gpt" + }, + "openai/gpt-5.3-chat": { + "id": "openai/gpt-5.3-chat", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "image", + "pdf", + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 16384, + "input": 111616 + }, + "family": "gpt" + }, + "openai/gpt-5.1-codex": { + "id": "openai/gpt-5.1-codex", + "reasoning": true, + "temperature": false, + "toolCall": false, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 400000, + "output": 128000, + "input": 400000 + }, + "family": "gpt-codex" + }, + "openai/gpt-5.2-pro": { + "id": "openai/gpt-5.2-pro", + "reasoning": true, + "temperature": false, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image", + "pdf" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 400000, + "output": 128000, + "input": 400000 + }, + "family": "gpt-pro" + }, + "openai/gpt-5.4-nano": { + "id": "openai/gpt-5.4-nano", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image", + "pdf" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 400000, + "output": 128000, + "input": 272000 + }, + "family": "gpt" + }, + "openai/gpt-5.4-mini": { + "id": "openai/gpt-5.4-mini", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image", + "pdf" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 400000, + "output": 128000, + "input": 272000 + }, + "family": "gpt" + }, + "minimax/minimax-m2.5-lightning": { + "id": "minimax/minimax-m2.5-lightning", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 204800, + "output": 131072 + } + }, + "minimax/minimax-m2.1": { + "id": "minimax/minimax-m2.1", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 200000, + "output": 131072, + "input": 200000 + }, + "family": "minimax" + }, + "minimax/minimax-m2.7": { + "id": "minimax/minimax-m2.7", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 204800, + "output": 131072, + "input": 204800 + }, + "family": "minimax" + }, + "minimax/minimax-m2.7-highspeed": { + "id": "minimax/minimax-m2.7-highspeed", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 204800, + "output": 131100 + }, + "family": "minimax" + }, + "minimax/minimax-m2": { + "id": "minimax/minimax-m2", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 196608, + "output": 196608 + }, + "family": "minimax" + }, + "minimax/minimax-m2.5": { + "id": "MiniMax/MiniMax-M2.5", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 204800, + "output": 131072, + "input": 204800 + }, + "family": "minimax" + }, + "anthropic/claude-3.5-sonnet": { + "id": "anthropic/claude-3.5-sonnet", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "image", + "pdf", + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 200000, + "output": 8192 + }, + "family": "claude-sonnet" + }, + "anthropic/claude-3.7-sonnet": { + "id": "anthropic/claude-3.7-sonnet", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "image", + "pdf", + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 200000, + "output": 64000 + }, + "family": "claude-sonnet" + }, + "anthropic/claude-opus-4.1": { + "id": "anthropic/claude-opus-4.1", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "image", + "pdf", + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 200000, + "output": 32000 + }, + "family": "claude-opus" + }, + "anthropic/claude-sonnet-4.6": { + "id": "anthropic/claude-sonnet-4.6", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image", + "pdf" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 1000000, + "output": 128000, + "input": 1000000 + }, + "family": "claude-sonnet" + }, + "anthropic/claude-haiku-4.5": { + "id": "anthropic/claude-haiku-4.5", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "image", + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 200000, + "output": 64000 + }, + "family": "claude-haiku" + }, + "anthropic/claude-3.5-haiku": { + "id": "anthropic/claude-3.5-haiku", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 200000, + "output": 8192 + }, + "family": "claude-haiku" + }, + "anthropic/claude-opus-4.5": { + "id": "anthropic/claude-opus-4.5", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "image", + "pdf", + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 200000, + "output": 64000 + }, + "family": "claude-opus" + }, + "anthropic/claude-opus-4": { + "id": "anthropic/claude-opus-4", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "image", + "pdf", + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 200000, + "output": 32000 + }, + "family": "claude-opus" + }, + "anthropic/claude-sonnet-4": { + "id": "anthropic/claude-sonnet-4", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "image", + "pdf", + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 200000, + "output": 64000 + }, + "family": "claude-sonnet" + }, + "anthropic/claude-sonnet-4.5": { + "id": "anthropic/claude-sonnet-4.5", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "image", + "pdf", + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 1000000, + "output": 64000 + }, + "family": "claude-sonnet" + }, + "anthropic/claude-opus-4.6": { + "id": "anthropic/claude-opus-4.6", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image", + "pdf" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 1000000, + "output": 128000, + "input": 1000000 + }, + "family": "claude-opus" + }, + "zai-org/glm-4.6": { + "id": "zai-org/GLM-4.6", + "family": "glm", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 202752, + "output": 131072 + } + }, + "deepseek-ai/deepseek-r1-0528": { + "id": "deepseek-ai/DeepSeek-R1-0528", + "family": "deepseek", + "reasoning": true, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 163840, + "input": 128000 + } + }, + "intel/qwen3-coder-480b-a35b-instruct-int4-mixed-ar": { + "id": "Intel/Qwen3-Coder-480B-A35B-Instruct-int4-mixed-ar", + "family": "qwen", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 106000, + "output": 4096 + } + }, + "moonshotai/kimi-k2-instruct-0905": { + "id": "moonshotai/Kimi-K2-Instruct-0905", + "family": "kimi", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 262144, + "output": 262144, + "input": 256000 + } + }, + "meta-llama/llama-3.2-90b-vision-instruct": { + "id": "meta-llama/llama-3.2-90b-vision-instruct", + "family": "llama", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 131072, + "output": 16384, + "input": 131072 + } + }, + "meta-llama/llama-3.3-70b-instruct": { + "id": "meta-llama/llama-3.3-70b-instruct", + "family": "llama", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 131072, + "output": 16384, + "input": 131072 + } + }, + "meta-llama/llama-4-maverick-17b-128e-instruct-fp8": { + "id": "meta-llama/Llama-4-Maverick-17B-128E-Instruct-FP8", + "family": "llama", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 1000000, + "output": 32768 + } + }, + "qwen/qwen3-next-80b-a3b-instruct": { + "id": "Qwen/Qwen3-Next-80B-A3B-Instruct", + "family": "qwen", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 262144, + "output": 262144 + } + }, + "qwen/qwen3-235b-a22b-thinking-2507": { + "id": "Qwen/Qwen3-235B-A22B-Thinking-2507", + "family": "qwen", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 262144, + "output": 262144 + } + }, + "qwen/qwen2.5-vl-32b-instruct": { + "id": "Qwen/Qwen2.5-VL-32B-Instruct", + "family": "qwen", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 16384, + "output": 16384 + } + }, + "mistralai/mistral-nemo-instruct-2407": { + "id": "mistralai/Mistral-Nemo-Instruct-2407", + "family": "mistral-nemo", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 16384, + "output": 8192, + "input": 16384 + } + }, + "mistralai/magistral-small-2506": { + "id": "mistralai/Magistral-Small-2506", + "family": "magistral-small", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 4096 + } + }, + "mistralai/mistral-large-instruct-2411": { + "id": "mistralai/Mistral-Large-Instruct-2411", + "family": "mistral-large", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 4096 + } + }, + "mistralai/devstral-small-2505": { + "id": "mistralai/Devstral-Small-2505", + "family": "devstral", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 32768, + "output": 8192, + "input": 32768 + } + }, + "openai/gpt-oss-20b": { + "id": "openai/gpt-oss-20b", + "family": "gpt-oss", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 131072, + "output": 131072, + "input": 128000 + } + }, + "nvidia/nemotron-3-super-120b-a12b": { + "id": "nvidia/nemotron-3-super-120b-a12b", + "family": "nemotron", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 262144, + "output": 262144, + "input": 256000 + } + }, + "nvidia/llama-3.1-nemotron-70b-instruct": { + "id": "nvidia/llama-3.1-nemotron-70b-instruct", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 131072, + "output": 16384 + } + }, + "nvidia/llama-3.1-nemotron-ultra-253b-v1": { + "id": "nvidia/Llama-3.1-Nemotron-Ultra-253B-v1", + "family": "nemotron", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 16384, + "input": 128000 + } + }, + "nvidia/llama-3.1-nemotron-51b-instruct": { + "id": "nvidia/llama-3.1-nemotron-51b-instruct", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 4096 + } + }, + "nvidia/parakeet-tdt-0.6b-v2": { + "id": "nvidia/parakeet-tdt-0.6b-v2", + "family": "parakeet", + "reasoning": false, + "temperature": false, + "toolCall": false, + "modalities": { + "input": [ + "audio" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 0, + "output": 4096 + } + }, + "nvidia/nvidia-nemotron-nano-9b-v2": { + "id": "nvidia/nvidia-nemotron-nano-9b-v2", + "family": "nemotron", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 16384, + "input": 128000 + } + }, + "nvidia/llama-embed-nemotron-8b": { + "id": "nvidia/llama-embed-nemotron-8b", + "family": "llama", + "reasoning": false, + "temperature": false, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 32768, + "output": 2048 + } + }, + "nvidia/llama-3.3-nemotron-super-49b-v1.5": { + "id": "nvidia/llama-3.3-nemotron-super-49b-v1.5", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 131072, + "output": 26215 + } + }, + "nvidia/llama-3.3-nemotron-super-49b-v1": { + "id": "nvidia/Llama-3.3-Nemotron-Super-49B-v1", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 16384, + "input": 128000 + }, + "family": "nemotron" + }, + "nvidia/llama3-chatqa-1.5-70b": { + "id": "nvidia/llama3-chatqa-1.5-70b", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 4096 + } + }, + "nvidia/cosmos-nemotron-34b": { + "id": "nvidia/cosmos-nemotron-34b", + "family": "nemotron", + "reasoning": true, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text", + "image", + "video" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 131072, + "output": 8192 + } + }, + "nvidia/nemoretriever-ocr-v1": { + "id": "nvidia/nemoretriever-ocr-v1", + "family": "nemoretriever", + "reasoning": false, + "temperature": false, + "toolCall": false, + "modalities": { + "input": [ + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 0, + "output": 4096 + } + }, + "nvidia/nemotron-4-340b-instruct": { + "id": "nvidia/nemotron-4-340b-instruct", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 4096 + } + }, + "nvidia/nemotron-3-nano-30b-a3b": { + "id": "nvidia/nemotron-3-nano-30b-a3b", + "family": "nemotron", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 256000, + "output": 262144, + "input": 256000 + } + }, + "microsoft/phi-3-small-128k-instruct": { + "id": "microsoft/phi-3-small-128k-instruct", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 4096 + }, + "family": "phi" + }, + "microsoft/phi-3-medium-128k-instruct": { + "id": "microsoft/phi-3-medium-128k-instruct", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 4096 + }, + "family": "phi" + }, + "microsoft/phi-3.5-moe-instruct": { + "id": "microsoft/phi-3.5-moe-instruct", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 4096 + }, + "family": "phi" + }, + "microsoft/phi-3-vision-128k-instruct": { + "id": "microsoft/phi-3-vision-128k-instruct", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 4096 + } + }, + "microsoft/phi-4-mini-instruct": { + "id": "microsoft/phi-4-mini-instruct", + "family": "phi", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 4096 + } + }, + "microsoft/phi-3.5-vision-instruct": { + "id": "microsoft/phi-3.5-vision-instruct", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 4096 + }, + "family": "phi" + }, + "microsoft/phi-3-medium-4k-instruct": { + "id": "microsoft/phi-3-medium-4k-instruct", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 4096, + "output": 1024 + }, + "family": "phi" + }, + "microsoft/phi-3-small-8k-instruct": { + "id": "microsoft/phi-3-small-8k-instruct", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 8192, + "output": 2048 + }, + "family": "phi" + }, + "minimaxai/minimax-m2.1": { + "id": "MiniMaxAI/MiniMax-M2.1", + "family": "minimax", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 196608, + "output": 196608, + "input": 120000 + } + }, + "minimaxai/minimax-m2.5": { + "id": "MiniMaxAI/MiniMax-M2.5", + "family": "minimax", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 196608, + "output": 196608 + } + }, + "deepseek-ai/deepseek-v3.1": { + "id": "deepseek-ai/DeepSeek-V3.1", + "family": "deepseek", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text", + "pdf" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 65536, + "input": 128000 + } + }, + "deepseek-ai/deepseek-r1": { + "id": "deepseek-ai/DeepSeek-R1", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 164000, + "output": 164000 + }, + "family": "deepseek-thinking" + }, + "deepseek-ai/deepseek-v3.1-terminus": { + "id": "deepseek-ai/DeepSeek-V3.1-Terminus", + "family": "deepseek", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 65536, + "input": 128000 + } + }, + "deepseek-ai/deepseek-coder-6.7b-instruct": { + "id": "deepseek-ai/deepseek-coder-6.7b-instruct", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 4096 + } + }, + "deepseek-ai/deepseek-v3.2": { + "id": "deepseek-ai/DeepSeek-V3.2", + "family": "deepseek", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 163840, + "output": 65536, + "input": 160000 + } + }, + "moonshotai/kimi-k2-instruct": { + "id": "moonshotai/kimi-k2-instruct", + "family": "kimi", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 256000, + "output": 8192, + "input": 256000 + } + }, + "google/codegemma-7b": { + "id": "google/codegemma-7b", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 4096 + } + }, + "google/gemma-2-2b-it": { + "id": "google/gemma-2-2b-it", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 8192, + "output": 4096, + "input": 8000 + } + }, + "google/gemma-3-1b-it": { + "id": "google/gemma-3-1b-it", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 4096 + } + }, + "google/gemma-2-27b-it": { + "id": "google/gemma-2-27b-it", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 8192, + "output": 2048 + } + }, + "google/gemma-3n-e2b-it": { + "id": "google/gemma-3n-e2b-it", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 4096 + } + }, + "google/codegemma-1.1-7b": { + "id": "google/codegemma-1.1-7b", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 4096 + } + }, + "google/gemma-3n-e4b-it": { + "id": "google/gemma-3n-e4b-it", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 32768, + "output": 6554 + }, + "family": "gemma" + }, + "google/gemma-3-12b-it": { + "id": "google/gemma-3-12b-it", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "image", + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 131072, + "output": 131072 + }, + "family": "gemma" + }, + "google/gemma-3-27b-it": { + "id": "google/gemma-3-27b-it", + "family": "gemma", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "image", + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 65536, + "input": 100000 + } + }, + "z-ai/glm4.7": { + "id": "z-ai/glm4.7", + "family": "glm", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 204800, + "output": 131072 + } + }, + "z-ai/glm5": { + "id": "z-ai/glm5", + "family": "glm", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 202752, + "output": 131000 + } + }, + "stepfun-ai/step-3.5-flash": { + "id": "stepfun-ai/step-3.5-flash", + "reasoning": true, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 256000, + "output": 256000, + "input": 256000 + }, + "family": "step" + }, + "qwen/qwen3-next-80b-a3b-thinking": { + "id": "qwen/qwen3-next-80b-a3b-thinking", + "family": "qwen", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 131072, + "output": 32768, + "input": 120000 + } + }, + "qwen/qwen3-coder-480b-a35b-instruct": { + "id": "Qwen/Qwen3-Coder-480B-A35B-Instruct", + "family": "qwen", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 262144, + "output": 66536 + } + }, + "qwen/qwq-32b": { + "id": "qwen/qwq-32b", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 32768, + "output": 32768 + }, + "family": "qwen" + }, + "qwen/qwen2.5-coder-7b-instruct": { + "id": "qwen/qwen2.5-coder-7b-instruct", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 32768, + "output": 6554 + } + }, + "qwen/qwen3.5-397b-a17b": { + "id": "qwen/qwen3.5-397b-a17b", + "family": "qwen", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text", + "image", + "video" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 258048, + "output": 65536, + "input": 258048 + } + }, + "qwen/qwen2.5-coder-32b-instruct": { + "id": "Qwen/Qwen2.5-Coder-32B-Instruct", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 32768, + "output": 32768 + }, + "family": "qwen" + }, + "qwen/qwen3-235b-a22b": { + "id": "Qwen/Qwen3-235B-A22B", + "family": "qwen", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 40960, + "output": 40960 + } + }, + "meta/llama-3.1-70b-instruct": { + "id": "meta/llama-3.1-70b-instruct", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 4096 + } + }, + "meta/llama-3.3-70b-instruct": { + "id": "meta/llama-3.3-70b-instruct", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 32768 + }, + "family": "llama" + }, + "meta/llama-4-scout-17b-16e-instruct": { + "id": "meta/llama-4-scout-17b-16e-instruct", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 8192 + }, + "family": "llama" + }, + "meta/llama-3.2-11b-vision-instruct": { + "id": "meta/llama-3.2-11b-vision-instruct", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image", + "audio" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 8192 + }, + "family": "llama" + }, + "meta/llama3-8b-instruct": { + "id": "meta/llama3-8b-instruct", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 4096 + } + }, + "meta/codellama-70b": { + "id": "meta/codellama-70b", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 4096 + } + }, + "meta/llama-3.2-1b-instruct": { + "id": "meta/llama-3.2-1b-instruct", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 16000, + "output": 4096 + }, + "family": "llama" + }, + "meta/llama-3.1-405b-instruct": { + "id": "meta/llama-3.1-405b-instruct", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 4096 + } + }, + "meta/llama3-70b-instruct": { + "id": "meta/llama3-70b-instruct", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 4096 + } + }, + "meta/llama-4-maverick-17b-128e-instruct": { + "id": "meta/llama-4-maverick-17b-128e-instruct", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 4096 + } + }, + "mistralai/mistral-large-3-675b-instruct-2512": { + "id": "mistralai/mistral-large-3-675b-instruct-2512", + "family": "mistral-large", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 262144, + "output": 256000, + "input": 262144 + } + }, + "mistralai/mamba-codestral-7b-v0.1": { + "id": "mistralai/mamba-codestral-7b-v0.1", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 4096 + } + }, + "mistralai/codestral-22b-instruct-v0.1": { + "id": "mistralai/codestral-22b-instruct-v0.1", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 4096 + } + }, + "mistralai/mistral-large-2-instruct": { + "id": "mistralai/mistral-large-2-instruct", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 4096 + } + }, + "mistralai/ministral-14b-instruct-2512": { + "id": "mistralai/ministral-14b-instruct-2512", + "family": "ministral", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 262144, + "output": 32768, + "input": 262144 + } + }, + "mistralai/mistral-small-3.1-24b-instruct-2503": { + "id": "mistralai/mistral-small-3.1-24b-instruct-2503", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 4096 + } + }, + "mistralai/devstral-2-123b-instruct-2512": { + "id": "mistralai/devstral-2-123b-instruct-2512", + "family": "devstral", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 262144, + "output": 65536, + "input": 262144 + } + }, + "black-forest-labs/flux.1-dev": { + "id": "black-forest-labs/flux.1-dev", + "family": "flux", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "image" + ] + }, + "limit": { + "context": 4096, + "output": 0 + } + }, + "deepseek-ai/deepseek-r1-distill-llama-70b": { + "id": "deepseek-ai/DeepSeek-R1-Distill-Llama-70B", + "family": "deepseek-thinking", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 131072, + "output": 131072 + } + }, + "moonshotai/kimi-k2": { + "id": "moonshotai/kimi-k2", + "family": "kimi", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 131000, + "output": 26215 + } + }, + "qwen/qwen3-coder": { + "id": "qwen/qwen3-coder", + "family": "qwen", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 262144, + "output": 52429 + } + }, + "openai/gpt-4.1": { + "id": "openai/gpt-4.1", + "family": "gpt", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image", + "pdf" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 1047576, + "output": 32768, + "input": 1047576 + } + }, + "openai/gpt-5-mini": { + "id": "openai/gpt-5-mini", + "family": "gpt-mini", + "reasoning": true, + "temperature": false, + "toolCall": false, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 400000, + "output": 128000, + "input": 400000 + } + }, + "openai/gpt-5-nano": { + "id": "openai/gpt-5-nano", + "family": "gpt-nano", + "reasoning": true, + "temperature": false, + "toolCall": false, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 400000, + "output": 128000, + "input": 400000 + } + }, + "kimi-k2": { + "id": "kimi-k2", + "family": "kimi", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 262144, + "output": 262144 + } + }, + "qwen3-max-preview": { + "id": "qwen3-max-preview", + "family": "qwen", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 256000, + "output": 64000 + } + }, + "deepseek-v3": { + "id": "deepseek-v3", + "family": "deepseek", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 65536, + "output": 8192 + } + }, + "kimi-k2-0905": { + "id": "kimi-k2-0905", + "family": "kimi", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 262144, + "output": 16384 + } + }, + "qwen3-235b-a22b-instruct": { + "id": "qwen3-235b-a22b-instruct", + "family": "qwen", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 256000, + "output": 64000 + } + }, + "deepseek-r1": { + "id": "deepseek-r1", + "family": "deepseek-thinking", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 131072, + "output": 16384, + "input": 128000 + } + }, + "qwen3-32b": { + "id": "qwen3-32b", + "family": "qwen", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 131072, + "output": 16384 + } + }, + "deepseek-v3.2": { + "id": "deepseek-v3.2", + "family": "deepseek", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 128000 + } + }, + "qwen3-235b": { + "id": "qwen3-235b", + "family": "qwen", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 32000 + } + }, + "qwen3-vl-plus": { + "id": "qwen3-vl-plus", + "family": "qwen", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 262144, + "output": 32768 + } + }, + "qwen3-235b-a22b-thinking-2507": { + "id": "qwen3-235b-a22b-thinking-2507", + "family": "qwen", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 16384 + } + }, + "qwen3-max": { + "id": "qwen3-max", + "family": "qwen", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 262144, + "output": 65536 + } + }, + "qwen/qwen3-30b-a3b-instruct-2507": { + "id": "Qwen/Qwen3-30B-A3B-Instruct-2507", + "family": "qwen", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 262144, + "output": 262144, + "input": 120000 + } + }, + "qwen/qwen3-30b-a3b-thinking-2507": { + "id": "qwen/qwen3-30b-a3b-thinking-2507", + "family": "qwen", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 32768, + "output": 6554, + "input": 120000 + } + }, + "qwen/qwen3-coder-30b-a3b-instruct": { + "id": "qwen/qwen3-coder-30b-a3b-instruct", + "family": "qwen", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 160000, + "output": 32768, + "input": 120000 + } + }, + "qwen/qwen3-235b-a22b-instruct-2507": { + "id": "Qwen/Qwen3-235B-A22B-Instruct-2507", + "family": "qwen", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 262144, + "output": 262144 + } + }, + "zhipuai/glm-4.6": { + "id": "ZhipuAI/GLM-4.6", + "family": "glm", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 202752, + "output": 98304 + } + }, + "zhipuai/glm-4.5": { + "id": "ZhipuAI/GLM-4.5", + "family": "glm", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 131072, + "output": 98304 + } + }, + "cerebras-llama-4-maverick-17b-128e-instruct": { + "id": "cerebras-llama-4-maverick-17b-128e-instruct", + "family": "llama", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 4096 + } + }, + "llama-4-scout-17b-16e-instruct-fp8": { + "id": "llama-4-scout-17b-16e-instruct-fp8", + "family": "llama", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 4096 + } + }, + "llama-3.3-8b-instruct": { + "id": "llama-3.3-8b-instruct", + "family": "llama", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 4096 + } + }, + "groq-llama-4-maverick-17b-128e-instruct": { + "id": "groq-llama-4-maverick-17b-128e-instruct", + "family": "llama", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 4096 + } + }, + "llama-3.3-70b-instruct": { + "id": "llama-3.3-70b-instruct", + "family": "llama", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 32768 + } + }, + "cerebras-llama-4-scout-17b-16e-instruct": { + "id": "cerebras-llama-4-scout-17b-16e-instruct", + "family": "llama", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 4096 + } + }, + "llama-4-maverick-17b-128e-instruct-fp8": { + "id": "llama-4-maverick-17b-128e-instruct-fp8", + "family": "llama", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 8192 + } + }, + "mistral/mistral-nemo-12b-instruct": { + "id": "mistral/mistral-nemo-12b-instruct", + "family": "mistral-nemo", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 16000, + "output": 4096 + } + }, + "google/gemma-3": { + "id": "google/gemma-3", + "family": "gemma", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 125000, + "output": 4096 + } + }, + "qwen/qwen3-embedding-4b": { + "id": "Qwen/Qwen3-Embedding-4B", + "family": "qwen", + "reasoning": false, + "temperature": false, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 32000, + "output": 2048 + } + }, + "qwen/qwen-2.5-7b-vision-instruct": { + "id": "qwen/qwen-2.5-7b-vision-instruct", + "family": "qwen", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 125000, + "output": 4096 + } + }, + "meta/llama-3.2-3b-instruct": { + "id": "meta/llama-3.2-3b-instruct", + "family": "llama", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 16000, + "output": 4096 + } + }, + "meta/llama-3.1-8b-instruct": { + "id": "meta/llama-3.1-8b-instruct", + "family": "llama", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 16000, + "output": 4096 + } + }, + "osmosis/osmosis-structure-0.6b": { + "id": "osmosis/osmosis-structure-0.6b", + "family": "osmosis", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 4000, + "output": 2048 + } + }, + "zai-org/glm-4.7-flash": { + "id": "zai-org/GLM-4.7-Flash", + "family": "glm-flash", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 202752, + "output": 65535, + "input": 200000 + } + }, + "zai-org/glm-4.7": { + "id": "zai-org/glm-4.7", + "family": "glm", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 200000, + "output": 128000, + "input": 200000 + } + }, + "zai-org/glm-4.6v": { + "id": "zai-org/GLM-4.6V", + "family": "glm", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 131072, + "output": 65536 + } + }, + "zai-org/glm-4.5": { + "id": "zai-org/glm-4.5", + "family": "glm", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 131072, + "output": 98304, + "input": 124000 + } + }, + "zai-org/glm-5": { + "id": "zai-org/glm-5", + "family": "glm", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 200000, + "output": 128000, + "input": 200000 + } + }, + "minimaxai/minimax-m2": { + "id": "MiniMaxAI/MiniMax-M2", + "family": "minimax", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 262144, + "output": 32768 + } + }, + "meta-llama/llama-3.1-8b-instruct-turbo": { + "id": "meta-llama/Llama-3.1-8B-Instruct-Turbo", + "family": "llama", + "reasoning": false, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 131072, + "output": 16384 + } + }, + "meta-llama/llama-3.1-70b-instruct-turbo": { + "id": "meta-llama/Llama-3.1-70B-Instruct-Turbo", + "family": "llama", + "reasoning": false, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 131072, + "output": 16384 + } + }, + "meta-llama/llama-4-scout-17b-16e-instruct": { + "id": "meta-llama/Llama-4-Scout-17B-16E-Instruct", + "family": "llama", + "reasoning": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 64000, + "output": 64000 + }, + "temperature": true + }, + "meta-llama/llama-3.1-70b-instruct": { + "id": "meta-llama/llama-3.1-70b-instruct", + "family": "llama", + "reasoning": false, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 131072, + "output": 26215 + }, + "temperature": true + }, + "meta-llama/llama-3.1-8b-instruct": { + "id": "meta-llama/llama-3.1-8b-instruct", + "family": "llama", + "reasoning": false, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 131072, + "output": 16384, + "input": 131072 + }, + "temperature": true + }, + "meta-llama/llama-3.3-70b-instruct-turbo": { + "id": "meta-llama/Llama-3.3-70B-Instruct-Turbo", + "family": "llama", + "reasoning": false, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 131072, + "output": 131072 + }, + "temperature": true + }, + "qwen/qwen3-coder-480b-a35b-instruct-turbo": { + "id": "Qwen/Qwen3-Coder-480B-A35B-Instruct-Turbo", + "family": "qwen", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 262144, + "output": 66536 + } + }, + "anthropic/claude-3-7-sonnet-latest": { + "id": "anthropic/claude-3-7-sonnet-latest", + "family": "claude-sonnet", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 200000, + "output": 64000 + } + }, + "anthropic/claude-4-opus": { + "id": "anthropic/claude-4-opus", + "family": "claude-opus", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 200000, + "output": 32000 + } + }, + "perplexity/sonar": { + "id": "perplexity/sonar", + "family": "sonar", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 127072, + "output": 25415 + } + }, + "anthropic/claude-opus-4-6": { + "id": "anthropic/claude-opus-4-6", + "family": "claude-opus", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image", + "pdf" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 1000000, + "output": 128000 + } + }, + "anthropic/claude-sonnet-4-6": { + "id": "anthropic/claude-sonnet-4-6", + "family": "claude-sonnet", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 1000000, + "output": 128000 + } + }, + "anthropic/claude-haiku-4-5": { + "id": "anthropic/claude-haiku-4-5", + "family": "claude-haiku", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image", + "pdf" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 200000, + "output": 62000 + } + }, + "anthropic/claude-opus-4-5": { + "id": "anthropic/claude-opus-4-5", + "family": "claude-opus", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image", + "pdf" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 200000, + "output": 64000 + } + }, + "anthropic/claude-sonnet-4-5": { + "id": "anthropic/claude-sonnet-4-5", + "family": "claude-sonnet", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image", + "pdf" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 1000000, + "output": 64000 + } + }, + "xai/grok-4-1-fast-non-reasoning": { + "id": "xai/grok-4-1-fast-non-reasoning", + "family": "grok", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 2000000, + "output": 30000 + } + }, + "mimo-v2-omni": { + "id": "mimo-v2-omni", + "family": "mimo", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image", + "audio", + "video", + "pdf" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 256000, + "output": 128000 + } + }, + "mimo-v2-flash": { + "id": "mimo-v2-flash", + "family": "mimo", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 256000, + "output": 256000 + } + }, + "mimo-v2-pro": { + "id": "mimo-v2-pro", + "family": "mimo", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 1000000, + "output": 128000 + } + }, + "hf:minimaxai/minimax-m2.5": { + "id": "hf:MiniMaxAI/MiniMax-M2.5", + "family": "minimax", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 191488, + "output": 65536 + } + }, + "hf:minimaxai/minimax-m2": { + "id": "hf:MiniMaxAI/MiniMax-M2", + "family": "minimax", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 196608, + "output": 131000 + } + }, + "hf:minimaxai/minimax-m2.1": { + "id": "hf:MiniMaxAI/MiniMax-M2.1", + "family": "minimax", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 204800, + "output": 131072 + } + }, + "hf:deepseek-ai/deepseek-r1": { + "id": "hf:deepseek-ai/DeepSeek-R1", + "family": "deepseek-thinking", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 128000 + } + }, + "hf:deepseek-ai/deepseek-r1-0528": { + "id": "hf:deepseek-ai/DeepSeek-R1-0528", + "family": "deepseek-thinking", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 128000 + } + }, + "hf:deepseek-ai/deepseek-v3.1": { + "id": "hf:deepseek-ai/DeepSeek-V3.1", + "family": "deepseek", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 128000 + } + }, + "hf:deepseek-ai/deepseek-v3.2": { + "id": "hf:deepseek-ai/DeepSeek-V3.2", + "family": "deepseek", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 162816, + "input": 162816, + "output": 8000 + } + }, + "hf:deepseek-ai/deepseek-v3-0324": { + "id": "hf:deepseek-ai/DeepSeek-V3-0324", + "family": "deepseek", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 128000 + } + }, + "hf:deepseek-ai/deepseek-v3": { + "id": "hf:deepseek-ai/DeepSeek-V3", + "family": "deepseek", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 128000 + } + }, + "hf:deepseek-ai/deepseek-v3.1-terminus": { + "id": "hf:deepseek-ai/DeepSeek-V3.1-Terminus", + "family": "deepseek", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 128000 + } + }, + "hf:moonshotai/kimi-k2-instruct-0905": { + "id": "hf:moonshotai/Kimi-K2-Instruct-0905", + "family": "kimi", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 262144, + "output": 32768 + } + }, + "hf:moonshotai/kimi-k2.5": { + "id": "hf:moonshotai/Kimi-K2.5", + "family": "kimi", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 262144, + "output": 65536 + } + }, + "hf:moonshotai/kimi-k2-thinking": { + "id": "hf:moonshotai/Kimi-K2-Thinking", + "family": "kimi-thinking", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 262144, + "output": 262144 + } + }, + "hf:openai/gpt-oss-120b": { + "id": "hf:openai/gpt-oss-120b", + "family": "gpt-oss", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 32768 + } + }, + "hf:nvidia/kimi-k2.5-nvfp4": { + "id": "hf:nvidia/Kimi-K2.5-NVFP4", + "family": "kimi", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 262144, + "output": 65536 + } + }, + "hf:meta-llama/llama-4-scout-17b-16e-instruct": { + "id": "hf:meta-llama/Llama-4-Scout-17B-16E-Instruct", + "family": "llama", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 328000, + "output": 4096 + } + }, + "hf:meta-llama/llama-3.1-405b-instruct": { + "id": "hf:meta-llama/Llama-3.1-405B-Instruct", + "family": "llama", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 32768 + } + }, + "hf:meta-llama/llama-3.1-70b-instruct": { + "id": "hf:meta-llama/Llama-3.1-70B-Instruct", + "family": "llama", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 32768 + } + }, + "hf:meta-llama/llama-3.1-8b-instruct": { + "id": "hf:meta-llama/Llama-3.1-8B-Instruct", + "family": "llama", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 32768 + } + }, + "hf:meta-llama/llama-3.3-70b-instruct": { + "id": "hf:meta-llama/Llama-3.3-70B-Instruct", + "family": "llama", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "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", + "family": "llama", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 524000, + "output": 4096 + } + }, + "hf:zai-org/glm-4.7-flash": { + "id": "hf:zai-org/GLM-4.7-Flash", + "family": "glm", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 196608, + "output": 65536 + } + }, + "hf:zai-org/glm-4.6": { + "id": "hf:zai-org/GLM-4.6", + "family": "glm", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 200000, + "output": 64000 + } + }, + "hf:zai-org/glm-4.7": { + "id": "hf:zai-org/GLM-4.7", + "family": "glm", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 200000, + "output": 64000 + } + }, + "hf:qwen/qwen3-235b-a22b-thinking-2507": { + "id": "hf:Qwen/Qwen3-235B-A22B-Thinking-2507", + "family": "qwen", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 256000, + "output": 32000 + } + }, + "hf:qwen/qwen2.5-coder-32b-instruct": { + "id": "hf:Qwen/Qwen2.5-Coder-32B-Instruct", + "family": "qwen", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 32768, + "output": 32768 + } + }, + "hf:qwen/qwen3-coder-480b-a35b-instruct": { + "id": "hf:Qwen/Qwen3-Coder-480B-A35B-Instruct", + "family": "qwen", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 256000, + "output": 32000 + } + }, + "hf:qwen/qwen3-235b-a22b-instruct-2507": { + "id": "hf:Qwen/Qwen3-235B-A22B-Instruct-2507", + "family": "qwen", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 256000, + "output": 32000 + } + }, + "zai-org/glm-4.7-fp8": { + "id": "zai-org/GLM-4.7-FP8", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 202752, + "input": 124000, + "output": 65535 + } + }, + "zai-org/glm-4.5-air": { + "id": "zai-org/GLM-4.5-Air", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 131072, + "input": 124000, + "output": 131072 + }, + "family": "glm" + }, + "nvidia/llama-3_1-nemotron-ultra-253b-v1": { + "id": "nvidia/Llama-3_1-Nemotron-Ultra-253B-v1", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "input": 120000, + "output": 4096 + } + }, + "nvidia/nemotron-nano-v2-12b": { + "id": "nvidia/Nemotron-Nano-V2-12b", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 32000, + "input": 30000, + "output": 4096 + } + }, + "nvidia/nvidia-nemotron-3-nano-30b-a3b": { + "id": "nvidia/NVIDIA-Nemotron-3-Nano-30B-A3B", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 32000, + "input": 30000, + "output": 4096 + } + }, + "nousresearch/hermes-4-405b": { + "id": "nousresearch/hermes-4-405b", + "reasoning": true, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 131072, + "input": 120000, + "output": 26215 + }, + "family": "hermes" + }, + "nousresearch/hermes-4-70b": { + "id": "NousResearch/Hermes-4-70B", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 131072, + "input": 120000, + "output": 131072 + }, + "family": "nousresearch" + }, + "baai/bge-en-icl": { + "id": "BAAI/bge-en-icl", + "family": "text-embedding", + "reasoning": false, + "temperature": false, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 32768, + "input": 32768, + "output": 0 + } + }, + "baai/bge-multilingual-gemma2": { + "id": "BAAI/bge-multilingual-gemma2", + "family": "text-embedding", + "reasoning": false, + "temperature": false, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 8192, + "input": 8192, + "output": 0 + } + }, + "primeintellect/intellect-3": { + "id": "PrimeIntellect/INTELLECT-3", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "input": 120000, + "output": 8192 + } + }, + "deepseek-ai/deepseek-v3-0324-fast": { + "id": "deepseek-ai/DeepSeek-V3-0324-fast", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "input": 120000, + "output": 8192 + } + }, + "deepseek-ai/deepseek-v3-0324": { + "id": "deepseek-ai/DeepSeek-V3-0324", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 163840, + "input": 120000, + "output": 163840 + }, + "family": "deepseek" + }, + "deepseek-ai/deepseek-r1-0528-fast": { + "id": "deepseek-ai/DeepSeek-R1-0528-fast", + "family": "deepseek", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 131072, + "output": 8192 + } + }, + "intfloat/e5-mistral-7b-instruct": { + "id": "intfloat/e5-mistral-7b-instruct", + "family": "mistral", + "reasoning": false, + "temperature": false, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 4096, + "input": 32768, + "output": 4096 + } + }, + "moonshotai/kimi-k2.5-fast": { + "id": "moonshotai/Kimi-K2.5-fast", + "family": "kimi", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 256000, + "input": 256000, + "output": 8192 + } + }, + "google/gemma-3-27b-it-fast": { + "id": "google/gemma-3-27b-it-fast", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 110000, + "input": 100000, + "output": 8192 + } + }, + "google/gemma-2-9b-it-fast": { + "id": "google/gemma-2-9b-it-fast", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 8192, + "input": 8000, + "output": 4096 + } + }, + "meta-llama/meta-llama-3.1-8b-instruct": { + "id": "meta-llama/Meta-Llama-3.1-8B-Instruct", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 33000, + "input": 120000, + "output": 4000 + }, + "family": "llama" + }, + "meta-llama/llama-guard-3-8b": { + "id": "meta-llama/llama-guard-3-8b", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 131072, + "input": 8000, + "output": 26215 + } + }, + "meta-llama/llama-3.3-70b-instruct-fast": { + "id": "meta-llama/Llama-3.3-70B-Instruct-fast", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "input": 120000, + "output": 8192 + } + }, + "meta-llama/meta-llama-3.1-8b-instruct-fast": { + "id": "meta-llama/Meta-Llama-3.1-8B-Instruct-fast", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "input": 120000, + "output": 4096 + } + }, + "qwen/qwen3-32b": { + "id": "Qwen/Qwen3-32B", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 40960, + "input": 120000, + "output": 40960 + }, + "family": "qwen" + }, + "qwen/qwen2.5-vl-72b-instruct": { + "id": "qwen/qwen2.5-vl-72b-instruct", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "image", + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 32768, + "input": 120000, + "output": 32768 + }, + "family": "qwen" + }, + "qwen/qwen3-32b-fast": { + "id": "Qwen/Qwen3-32B-fast", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "input": 120000, + "output": 8192 + } + }, + "qwen/qwen2.5-coder-7b-fast": { + "id": "Qwen/Qwen2.5-Coder-7B-fast", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "input": 120000, + "output": 8192 + } + }, + "black-forest-labs/flux-dev": { + "id": "black-forest-labs/flux-dev", + "reasoning": false, + "temperature": false, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "image" + ] + }, + "limit": { + "context": 77, + "input": 77, + "output": 0 + } + }, + "black-forest-labs/flux-schnell": { + "id": "black-forest-labs/flux-schnell", + "reasoning": false, + "temperature": false, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "image" + ] + }, + "limit": { + "context": 77, + "input": 77, + "output": 0 + } + }, + "claude-4.5-haiku": { + "id": "claude-4.5-haiku", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 200000, + "output": 8192 + }, + "family": "claude-haiku" + }, + "claude-3.5-sonnet": { + "id": "claude-3.5-sonnet", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 200000, + "output": 8200 + } + }, + "qwen3-235b-a22b-instruct-2507": { + "id": "qwen3-235b-a22b-instruct-2507", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 16384 + }, + "family": "qwen" + }, + "claude-3.7-sonnet": { + "id": "claude-3.7-sonnet", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 200000, + "output": 64000 + }, + "family": "claude-sonnet" + }, + "qwen3-next-80b-a3b-thinking": { + "id": "qwen3-next-80b-a3b-thinking", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 131072, + "output": 32768 + }, + "family": "qwen" + }, + "claude-4.0-sonnet": { + "id": "claude-4.0-sonnet", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 200000, + "output": 64000 + } + }, + "qwen-vl-max-2025-01-25": { + "id": "qwen-vl-max-2025-01-25", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image", + "audio", + "video" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 4096 + } + }, + "doubao-seed-1.6-thinking": { + "id": "doubao-seed-1.6-thinking", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "image", + "text", + "video" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 256000, + "output": 32000 + } + }, + "qwen3-coder-480b-a35b-instruct": { + "id": "qwen3-coder-480b-a35b-instruct", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 262144, + "output": 65536 + }, + "family": "qwen" + }, + "claude-4.5-sonnet": { + "id": "claude-4.5-sonnet", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 200000, + "output": 64000 + }, + "family": "claude-sonnet" + }, + "qwen2.5-vl-7b-instruct": { + "id": "qwen2.5-vl-7b-instruct", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image", + "audio", + "video" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 8192 + } + }, + "doubao-seed-2.0-pro": { + "id": "doubao-seed-2.0-pro", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image", + "video" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 256000, + "output": 128000 + } + }, + "gemini-2.5-flash": { + "id": "gemini-2.5-flash", + "reasoning": true, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 1048756, + "output": 65536, + "input": 1048756 + }, + "family": "gemini-flash" + }, + "deepseek-v3.1": { + "id": "deepseek-v3.1", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 131072, + "output": 131072 + }, + "family": "deepseek" + }, + "doubao-seed-1.6": { + "id": "doubao-seed-1.6", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image", + "video" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 256000, + "output": 32000 + } + }, + "doubao-seed-2.0-mini": { + "id": "doubao-seed-2.0-mini", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image", + "video" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 256000, + "output": 32000 + } + }, + "claude-4.0-opus": { + "id": "claude-4.0-opus", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 200000, + "output": 32000 + } + }, + "qwen-turbo": { + "id": "qwen-turbo", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 1000000, + "output": 16384, + "input": 1000000 + }, + "family": "qwen" + }, + "gemini-3.0-pro-preview": { + "id": "gemini-3.0-pro-preview", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image", + "video", + "pdf", + "audio" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 1000000, + "output": 64000 + } + }, + "deepseek-r1-0528": { + "id": "deepseek-r1-0528", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 131072, + "output": 16384 + }, + "family": "deepseek-thinking" + }, + "doubao-1.5-vision-pro": { + "id": "doubao-1.5-vision-pro", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text", + "image", + "video" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 16000 + } + }, + "gemini-3.0-pro-image-preview": { + "id": "gemini-3.0-pro-image-preview", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text", + "image" + ] + }, + "limit": { + "context": 32768, + "output": 8192 + } + }, + "qwen3.5-397b-a17b": { + "id": "qwen3.5-397b-a17b", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image", + "video" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 262144, + "output": 65536 + }, + "family": "qwen" + }, + "gemini-2.5-flash-lite": { + "id": "gemini-2.5-flash-lite", + "reasoning": true, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 1048756, + "output": 65536, + "input": 1048756 + }, + "family": "gemini-flash-lite" + }, + "claude-3.5-haiku": { + "id": "claude-3.5-haiku", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 200000, + "output": 8192 + }, + "family": "claude-haiku" + }, + "gpt-oss-120b": { + "id": "gpt-oss-120b", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 128000 + }, + "family": "gpt-oss" + }, + "deepseek-v3-0324": { + "id": "deepseek-v3-0324", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 128000, + "input": 128000 + }, + "family": "deepseek" + }, + "doubao-1.5-pro-32k": { + "id": "doubao-1.5-pro-32k", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 32000, + "output": 8192, + "input": 32000 + } + }, + "qwen3-30b-a3b-instruct-2507": { + "id": "qwen3-30b-a3b-instruct-2507", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 256000, + "output": 32768, + "input": 256000 + } + }, + "qwen2.5-vl-72b-instruct": { + "id": "qwen2.5-vl-72b-instruct", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 32768, + "output": 32768 + } + }, + "qwen3-235b-a22b": { + "id": "qwen3-235b-a22b", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 131072, + "output": 16384 + }, + "family": "qwen" + }, + "doubao-seed-2.0-lite": { + "id": "doubao-seed-2.0-lite", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image", + "video" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 256000, + "output": 32000 + } + }, + "claude-4.1-opus": { + "id": "claude-4.1-opus", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 200000, + "output": 32000 + } + }, + "doubao-1.5-thinking-pro": { + "id": "doubao-1.5-thinking-pro", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 16000 + } + }, + "gemini-2.5-flash-image": { + "id": "gemini-2.5-flash-image", + "reasoning": true, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text", + "image" + ] + }, + "limit": { + "context": 32768, + "output": 32768 + }, + "family": "gemini-flash" + }, + "minimax-m1": { + "id": "MiniMax-M1", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 1000000, + "output": 131072, + "input": 1000000 + }, + "family": "minimax" + }, + "doubao-seed-1.6-flash": { + "id": "doubao-seed-1.6-flash", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image", + "video" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 256000, + "output": 32000 + } + }, + "qwen3-vl-30b-a3b-thinking": { + "id": "qwen3-vl-30b-a3b-thinking", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image", + "video" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 32000 + } + }, + "doubao-seed-2.0-code": { + "id": "doubao-seed-2.0-code", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image", + "video" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 256000, + "output": 128000 + } + }, + "qwen3-30b-a3b-thinking-2507": { + "id": "qwen3-30b-a3b-thinking-2507", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 126000, + "output": 32000 + } + }, + "claude-4.5-opus": { + "id": "claude-4.5-opus", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 200000, + "output": 64000 + }, + "family": "claude-opus" + }, + "gemini-2.0-flash-lite": { + "id": "gemini-2.0-flash-lite", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 1000000, + "output": 8192, + "input": 1000000 + }, + "family": "gemini-flash-lite" + }, + "qwen3-next-80b-a3b-instruct": { + "id": "qwen3-next-80b-a3b-instruct", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 131072, + "output": 32768 + }, + "family": "qwen" + }, + "gemini-3.0-flash-preview": { + "id": "gemini-3.0-flash-preview", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image", + "audio", + "video", + "pdf" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 1000000, + "output": 64000 + } + }, + "qwen3-30b-a3b": { + "id": "qwen3-30b-a3b", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 41000, + "output": 41000 + }, + "family": "qwen" + }, + "gpt-oss-20b": { + "id": "gpt-oss-20b", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 131072, + "output": 131072 + }, + "family": "gpt-oss" + }, + "kling-v2-6": { + "id": "kling-v2-6", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text", + "image", + "video" + ], + "output": [ + "video" + ] + }, + "limit": { + "context": 99999999, + "output": 99999999 + } + }, + "gemini-2.5-pro": { + "id": "gemini-2.5-pro", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 1048576, + "output": 65535, + "input": 1048756 + }, + "family": "gemini-pro" + }, + "gemini-2.0-flash": { + "id": "gemini-2.0-flash", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image", + "audio", + "video", + "pdf" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 1048576, + "output": 8192 + }, + "family": "gemini-flash" + }, + "qwen-max-2025-01-25": { + "id": "qwen-max-2025-01-25", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 4096 + } + }, + "deepseek/deepseek-v3.2-exp-thinking": { + "id": "deepseek/deepseek-v3.2-exp-thinking", + "reasoning": true, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 32000 + } + }, + "deepseek/deepseek-v3.1-terminus": { + "id": "deepseek/deepseek-v3.1-terminus", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 163840, + "output": 32768 + }, + "family": "deepseek" + }, + "deepseek/deepseek-v3.2-251201": { + "id": "deepseek/deepseek-v3.2-251201", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 32000 + } + }, + "deepseek/deepseek-math-v2": { + "id": "deepseek/deepseek-math-v2", + "reasoning": true, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 160000, + "output": 160000 + } + }, + "deepseek/deepseek-v3.1-terminus-thinking": { + "id": "deepseek/deepseek-v3.1-terminus-thinking", + "reasoning": true, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 32000 + } + }, + "z-ai/autoglm-phone-9b": { + "id": "z-ai/autoglm-phone-9b", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 12800, + "output": 4096 + } + }, + "stepfun-ai/gelab-zero-4b-preview": { + "id": "stepfun-ai/gelab-zero-4b-preview", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 8192, + "output": 4096 + } + }, + "meituan/longcat-flash-lite": { + "id": "meituan/longcat-flash-lite", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 256000, + "output": 320000 + } + }, + "meituan/longcat-flash-chat": { + "id": "meituan/longcat-flash-chat", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 131072, + "output": 131072 + }, + "family": "longcat" + }, + "x-ai/grok-4-fast-reasoning": { + "id": "x-ai/grok-4-fast-reasoning", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image", + "audio", + "video" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 2000000, + "output": 2000000 + } + }, + "x-ai/grok-4.1-fast-reasoning": { + "id": "x-ai/grok-4.1-fast-reasoning", + "reasoning": true, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 2000000, + "output": 131072, + "input": 2000000 + }, + "family": "grok" + }, + "x-ai/grok-4-fast-non-reasoning": { + "id": "x-ai/grok-4-fast-non-reasoning", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image", + "audio", + "video" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 2000000, + "output": 2000000 + } + }, + "minimax/minimax-m2.5-highspeed": { + "id": "minimax/minimax-m2.5-highspeed", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 0, + "output": 0 + }, + "family": "minimax" + }, + "qwen3-coder:480b": { + "id": "qwen3-coder:480b", + "family": "qwen", + "reasoning": false, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 262144, + "output": 65536 + } + }, + "nemotron-3-nano:30b": { + "id": "nemotron-3-nano:30b", + "family": "nemotron", + "reasoning": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 1048576, + "output": 131072 + } + }, + "ministral-3:8b": { + "id": "ministral-3:8b", + "family": "ministral", + "reasoning": false, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 262144, + "output": 128000 + } + }, + "gpt-oss:120b": { + "id": "gpt-oss:120b", + "family": "gpt-oss", + "reasoning": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 131072, + "output": 32768 + } + }, + "devstral-2:123b": { + "id": "devstral-2:123b", + "family": "devstral", + "reasoning": false, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 262144, + "output": 262144 + } + }, + "qwen3-vl:235b-instruct": { + "id": "qwen3-vl:235b-instruct", + "family": "qwen", + "reasoning": false, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 262144, + "output": 131072 + } + }, + "gemini-3-flash-preview": { + "id": "gemini-3-flash-preview", + "family": "gemini-flash", + "reasoning": false, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image", + "video", + "audio" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 1048576, + "output": 65536, + "input": 128000 + }, + "temperature": true + }, + "minimax-m2.1": { + "id": "minimax-m2.1", + "family": "minimax", + "reasoning": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 196000, + "output": 196000 + }, + "temperature": true + }, + "ministral-3:14b": { + "id": "ministral-3:14b", + "family": "ministral", + "reasoning": false, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 262144, + "output": 128000 + } + }, + "qwen3-next:80b": { + "id": "qwen3-next:80b", + "family": "qwen", + "reasoning": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 262144, + "output": 32768 + } + }, + "kimi-k2:1t": { + "id": "kimi-k2:1t", + "family": "kimi", + "reasoning": false, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 262144, + "output": 262144 + } + }, + "gemma3:12b": { + "id": "gemma3:12b", + "family": "gemma", + "reasoning": false, + "toolCall": false, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 131072, + "output": 131072 + } + }, + "minimax-m2.7": { + "id": "MiniMax-M2.7", + "family": "minimax", + "reasoning": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 204800, + "output": 131072 + }, + "temperature": true + }, + "gpt-oss:20b": { + "id": "gpt-oss:20b", + "family": "gpt-oss", + "reasoning": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 131072, + "output": 32768 + } + }, + "kimi-k2-thinking": { + "id": "kimi-k2-thinking", + "family": "kimi", + "reasoning": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 262144, + "output": 16384 + }, + "temperature": true + }, + "ministral-3:3b": { + "id": "ministral-3:3b", + "family": "ministral", + "reasoning": false, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 262144, + "output": 128000 + } + }, + "qwen3.5:397b": { + "id": "qwen3.5:397b", + "family": "qwen", + "reasoning": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 262144, + "output": 81920 + } + }, + "gemma3:27b": { + "id": "gemma3:27b", + "family": "gemma", + "reasoning": false, + "toolCall": false, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 131072, + "output": 131072 + } + }, + "minimax-m2": { + "id": "minimax-m2", + "family": "minimax", + "reasoning": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 400000, + "output": 400000, + "input": 200000 + }, + "temperature": true + }, + "devstral-small-2:24b": { + "id": "devstral-small-2:24b", + "family": "devstral", + "reasoning": false, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 262144, + "output": 262144 + } + }, + "nemotron-3-super": { + "id": "nemotron-3-super", + "family": "nemotron", + "reasoning": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 262144, + "output": 65536 + } + }, + "cogito-2.1:671b": { + "id": "cogito-2.1:671b", + "family": "cogito", + "reasoning": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 163840, + "output": 32000 + } + }, + "gemma3:4b": { + "id": "gemma3:4b", + "family": "gemma", + "reasoning": false, + "toolCall": false, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 131072, + "output": 131072 + } + }, + "deepseek-v3.1:671b": { + "id": "deepseek-v3.1:671b", + "family": "deepseek", + "reasoning": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 163840, + "output": 163840 + } + }, + "mistral-large-3:675b": { + "id": "mistral-large-3:675b", + "family": "mistral-large", + "reasoning": false, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 262144, + "output": 262144 + } + }, + "rnj-1:8b": { + "id": "rnj-1:8b", + "family": "rnj", + "reasoning": false, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 32768, + "output": 4096 + } + }, + "qwen3-vl:235b": { + "id": "qwen3-vl:235b", + "family": "qwen", + "reasoning": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 262144, + "output": 32768 + } + }, + "voxtral-small-24b-2507": { + "id": "voxtral-small-24b-2507", + "family": "voxtral", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "audio" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 32000, + "output": 16384 + } + }, + "mistral-small-3.2-24b-instruct-2506": { + "id": "mistral-small-3.2-24b-instruct-2506", + "family": "mistral-small", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 131072, + "output": 131072 + } + }, + "qwen3-embedding-8b": { + "id": "qwen3-embedding-8b", + "family": "qwen", + "reasoning": false, + "temperature": false, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 32768, + "output": 4096 + } + }, + "bge-multilingual-gemma2": { + "id": "bge-multilingual-gemma2", + "family": "gemma", + "reasoning": false, + "temperature": false, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 8191, + "output": 3072 + } + }, + "deepseek-r1-distill-llama-70b": { + "id": "deepseek-r1-distill-llama-70b", + "family": "deepseek-thinking", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 32768, + "output": 16384 + } + }, + "qwen3-coder-30b-a3b-instruct": { + "id": "qwen3-coder-30b-a3b-instruct", + "family": "qwen", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 262144, + "output": 65536, + "input": 128000 + } + }, + "whisper-large-v3": { + "id": "whisper-large-v3", + "family": "whisper", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "audio" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 0, + "output": 4096 + } + }, + "llama-3.1-8b-instruct": { + "id": "llama-3.1-8b-instruct", + "family": "llama", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 16384, + "output": 16384 + } + }, + "devstral-2-123b-instruct-2512": { + "id": "devstral-2-123b-instruct-2512", + "family": "devstral", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 256000, + "output": 16384 + } + }, + "pixtral-12b-2409": { + "id": "pixtral-12b-2409", + "family": "pixtral", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 4096 + } + }, + "mistral-nemo-instruct-2407": { + "id": "mistral-nemo-instruct-2407", + "family": "mistral-nemo", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 65536, + "output": 65536 + } + }, + "gemma-3-27b-it": { + "id": "Gemma-3-27B-it", + "family": "gemma", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 32768, + "output": 16384, + "input": 32768 + } + }, + "workers-ai/@cf/zai-org/glm-4.7-flash": { + "id": "workers-ai/@cf/zai-org/glm-4.7-flash", + "family": "glm-flash", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 131072, + "output": 131072 + } + }, + "workers-ai/@cf/nvidia/nemotron-3-120b-a12b": { + "id": "workers-ai/@cf/nvidia/nemotron-3-120b-a12b", + "family": "nemotron", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 256000, + "output": 256000 + } + }, + "workers-ai/@cf/ibm-granite/granite-4.0-h-micro": { + "id": "workers-ai/@cf/ibm-granite/granite-4.0-h-micro", + "family": "granite", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 16384 + } + }, + "workers-ai/@cf/baai/bge-small-en-v1.5": { + "id": "workers-ai/@cf/baai/bge-small-en-v1.5", + "family": "bge", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 16384 + } + }, + "workers-ai/@cf/baai/bge-large-en-v1.5": { + "id": "workers-ai/@cf/baai/bge-large-en-v1.5", + "family": "bge", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 16384 + } + }, + "workers-ai/@cf/baai/bge-reranker-base": { + "id": "workers-ai/@cf/baai/bge-reranker-base", + "family": "bge", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 16384 + } + }, + "workers-ai/@cf/baai/bge-m3": { + "id": "workers-ai/@cf/baai/bge-m3", + "family": "bge", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 16384 + } + }, + "workers-ai/@cf/baai/bge-base-en-v1.5": { + "id": "workers-ai/@cf/baai/bge-base-en-v1.5", + "family": "bge", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 16384 + } + }, + "workers-ai/@cf/pfnet/plamo-embedding-1b": { + "id": "workers-ai/@cf/pfnet/plamo-embedding-1b", + "family": "plamo", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 16384 + } + }, + "workers-ai/@cf/deepseek-ai/deepseek-r1-distill-qwen-32b": { + "id": "workers-ai/@cf/deepseek-ai/deepseek-r1-distill-qwen-32b", + "family": "deepseek-thinking", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 16384 + } + }, + "workers-ai/@cf/facebook/bart-large-cnn": { + "id": "workers-ai/@cf/facebook/bart-large-cnn", + "family": "bart", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 16384 + } + }, + "workers-ai/@cf/mistral/mistral-7b-instruct-v0.1": { + "id": "workers-ai/@cf/mistral/mistral-7b-instruct-v0.1", + "family": "mistral", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 16384 + } + }, + "workers-ai/@cf/myshell-ai/melotts": { + "id": "workers-ai/@cf/myshell-ai/melotts", + "family": "melotts", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 16384 + } + }, + "workers-ai/@cf/pipecat-ai/smart-turn-v2": { + "id": "workers-ai/@cf/pipecat-ai/smart-turn-v2", + "family": "smart-turn", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 16384 + } + }, + "workers-ai/@cf/moonshotai/kimi-k2.5": { + "id": "workers-ai/@cf/moonshotai/kimi-k2.5", + "family": "kimi", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 256000, + "output": 256000 + } + }, + "workers-ai/@cf/google/gemma-3-12b-it": { + "id": "workers-ai/@cf/google/gemma-3-12b-it", + "family": "gemma", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 16384 + } + }, + "workers-ai/@cf/qwen/qwq-32b": { + "id": "workers-ai/@cf/qwen/qwq-32b", + "family": "qwen", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 16384 + } + }, + "workers-ai/@cf/qwen/qwen3-30b-a3b-fp8": { + "id": "workers-ai/@cf/qwen/qwen3-30b-a3b-fp8", + "family": "qwen", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 16384 + } + }, + "workers-ai/@cf/qwen/qwen2.5-coder-32b-instruct": { + "id": "workers-ai/@cf/qwen/qwen2.5-coder-32b-instruct", + "family": "qwen", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 16384 + } + }, + "workers-ai/@cf/qwen/qwen3-embedding-0.6b": { + "id": "workers-ai/@cf/qwen/qwen3-embedding-0.6b", + "family": "qwen", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "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", + "family": "llama", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 16384 + } + }, + "workers-ai/@cf/meta/llama-3-8b-instruct-awq": { + "id": "workers-ai/@cf/meta/llama-3-8b-instruct-awq", + "family": "llama", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "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", + "family": "llama", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "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", + "family": "llama", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "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", + "family": "llama", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 16384 + } + }, + "workers-ai/@cf/meta/llama-3.2-3b-instruct": { + "id": "workers-ai/@cf/meta/llama-3.2-3b-instruct", + "family": "llama", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 16384 + } + }, + "workers-ai/@cf/meta/llama-guard-3-8b": { + "id": "workers-ai/@cf/meta/llama-guard-3-8b", + "family": "llama", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 16384 + } + }, + "workers-ai/@cf/meta/llama-3.2-1b-instruct": { + "id": "workers-ai/@cf/meta/llama-3.2-1b-instruct", + "family": "llama", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "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", + "family": "llama", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 16384 + } + }, + "workers-ai/@cf/meta/llama-3.1-8b-instruct": { + "id": "workers-ai/@cf/meta/llama-3.1-8b-instruct", + "family": "llama", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 16384 + } + }, + "workers-ai/@cf/meta/m2m100-1.2b": { + "id": "workers-ai/@cf/meta/m2m100-1.2b", + "family": "m2m", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 16384 + } + }, + "workers-ai/@cf/meta/llama-2-7b-chat-fp16": { + "id": "workers-ai/@cf/meta/llama-2-7b-chat-fp16", + "family": "llama", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 16384 + } + }, + "workers-ai/@cf/meta/llama-3-8b-instruct": { + "id": "workers-ai/@cf/meta/llama-3-8b-instruct", + "family": "llama", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "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", + "family": "mistral-small", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 16384 + } + }, + "workers-ai/@cf/deepgram/aura-2-es": { + "id": "workers-ai/@cf/deepgram/aura-2-es", + "family": "aura", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 16384 + } + }, + "workers-ai/@cf/deepgram/nova-3": { + "id": "workers-ai/@cf/deepgram/nova-3", + "family": "nova", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 16384 + } + }, + "workers-ai/@cf/deepgram/aura-2-en": { + "id": "workers-ai/@cf/deepgram/aura-2-en", + "family": "aura", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 16384 + } + }, + "workers-ai/@cf/openai/gpt-oss-120b": { + "id": "workers-ai/@cf/openai/gpt-oss-120b", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 16384 + } + }, + "workers-ai/@cf/openai/gpt-oss-20b": { + "id": "workers-ai/@cf/openai/gpt-oss-20b", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 16384 + } + }, + "workers-ai/@cf/ai4bharat/indictrans2-en-indic-1b": { + "id": "workers-ai/@cf/ai4bharat/indictrans2-en-indic-1B", + "family": "indictrans", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 16384 + } + }, + "workers-ai/@cf/huggingface/distilbert-sst-2-int8": { + "id": "workers-ai/@cf/huggingface/distilbert-sst-2-int8", + "family": "distilbert", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "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", + "family": "gemma", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 16384 + } + }, + "openai/gpt-4o-mini": { + "id": "openai/gpt-4o-mini", + "family": "gpt-mini", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 16384, + "input": 128000 + } + }, + "openai/o1": { + "id": "openai/o1", + "family": "o", + "reasoning": true, + "temperature": false, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 200000, + "output": 100000, + "input": 200000 + } + }, + "openai/o3": { + "id": "openai/o3", + "family": "o", + "reasoning": false, + "temperature": false, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 200000, + "output": 100000, + "input": 200000 + } + }, + "openai/gpt-3.5-turbo": { + "id": "openai/gpt-3.5-turbo", + "family": "gpt", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 16385, + "output": 4096, + "input": 16385 + } + }, + "openai/o3-pro": { + "id": "openai/o3-pro", + "family": "o-pro", + "reasoning": true, + "temperature": false, + "toolCall": true, + "modalities": { + "input": [ + "image", + "pdf", + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 200000, + "output": 100000, + "input": 100000 + } + }, + "openai/gpt-4-turbo": { + "id": "openai/gpt-4-turbo", + "family": "gpt", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 4096, + "input": 128000 + } + }, + "openai/o4-mini": { + "id": "openai/o4-mini", + "family": "o-mini", + "reasoning": true, + "temperature": false, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 200000, + "output": 100000, + "input": 200000 + } + }, + "openai/o3-mini": { + "id": "openai/o3-mini", + "family": "o-mini", + "reasoning": true, + "temperature": false, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 200000, + "output": 100000, + "input": 200000 + } + }, + "openai/gpt-4": { + "id": "openai/gpt-4", + "family": "gpt", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 8191, + "output": 4096 + } + }, + "openai/gpt-4o": { + "id": "openai/gpt-4o", + "family": "gpt", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 16384, + "input": 128000 + } + }, + "anthropic/claude-opus-4-1": { + "id": "anthropic/claude-opus-4-1", + "family": "claude-opus", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image", + "pdf" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 200000, + "output": 32000 + } + }, + "anthropic/claude-3-sonnet": { + "id": "anthropic/claude-3-sonnet", + "family": "claude-sonnet", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image", + "pdf" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 200000, + "output": 4096 + } + }, + "anthropic/claude-3-5-haiku": { + "id": "anthropic/claude-3-5-haiku", + "family": "claude-haiku", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image", + "pdf" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 200000, + "output": 8192 + } + }, + "anthropic/claude-3-haiku": { + "id": "anthropic/claude-3-haiku", + "family": "claude-haiku", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 200000, + "output": 4096 + } + }, + "anthropic/claude-3-opus": { + "id": "anthropic/claude-3-opus", + "family": "claude-opus", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image", + "pdf" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 200000, + "output": 4096 + } + }, + "solar-pro2": { + "id": "solar-pro2", + "family": "solar-pro", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 65536, + "output": 8192 + } + }, + "solar-mini": { + "id": "solar-mini", + "family": "solar-mini", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 32768, + "output": 4096 + } + }, + "solar-pro3": { + "id": "solar-pro3", + "family": "solar-pro", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 131072, + "output": 8192 + } + }, + "mercury-2": { + "id": "mercury-2", + "family": "mercury", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 50000 + } + }, + "mercury": { + "id": "mercury", + "family": "mercury", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 16384 + } + }, + "mercury-edit": { + "id": "mercury-edit", + "reasoning": true, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 8192 + } + }, + "mercury-coder": { + "id": "mercury-coder", + "family": "mercury", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 16384 + } + }, + "zai-org/glm-4.5-fp8": { + "id": "zai-org/GLM-4.5-FP8", + "family": "glm", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 131072, + "output": 65536 + } + }, + "qwen/qwen3-coder-480b-a35b-instruct-fp8": { + "id": "Qwen/Qwen3-Coder-480B-A35B-Instruct-FP8", + "family": "qwen", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 262144, + "output": 262144 + } + }, + "minimax-m2.7-highspeed": { + "id": "MiniMax-M2.7-highspeed", + "family": "minimax", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 204800, + "output": 131072 + } + }, + "minimax-m2.5-highspeed": { + "id": "MiniMax-M2.5-highspeed", + "family": "minimax", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 204800, + "output": 131072 + } + }, + "zai-org/autoglm-phone-9b-multilingual": { + "id": "zai-org/autoglm-phone-9b-multilingual", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 65536, + "output": 65536 + } + }, + "zai-org/glm-4.5v": { + "id": "zai-org/glm-4.5v", + "family": "glmv", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image", + "video" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 65536, + "output": 16384 + } + }, + "microsoft/wizardlm-2-8x22b": { + "id": "microsoft/wizardlm-2-8x22b", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text", + "pdf" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 65536, + "output": 8192, + "input": 65536 + }, + "family": "gpt" + }, + "minimaxai/minimax-m1-80k": { + "id": "MiniMaxAI/MiniMax-M1-80k", + "family": "minimax", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 1000000, + "output": 131072, + "input": 1000000 + } + }, + "skywork/r1v4-lite": { + "id": "skywork/r1v4-lite", + "family": "skywork", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 262144, + "output": 65536 + } + }, + "gryphe/mythomax-l2-13b": { + "id": "Gryphe/MythoMax-L2-13b", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 4000, + "output": 4096, + "input": 4000 + }, + "family": "llama" + }, + "paddlepaddle/paddleocr-vl": { + "id": "PaddlePaddle/PaddleOCR-VL", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 16384, + "output": 16384 + } + }, + "baichuan/baichuan-m2-32b": { + "id": "baichuan/baichuan-m2-32b", + "family": "baichuan", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 131072, + "output": 131072 + } + }, + "kwaipilot/kat-coder-pro": { + "id": "kwaipilot/kat-coder-pro", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 256000, + "output": 128000 + } + }, + "kwaipilot/kat-coder": { + "id": "kwaipilot/kat-coder", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 256000, + "output": 32000 + } + }, + "deepseek/deepseek-v3-turbo": { + "id": "deepseek/deepseek-v3-turbo", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 64000, + "output": 16000 + } + }, + "deepseek/deepseek-prover-v2-671b": { + "id": "deepseek/deepseek-prover-v2-671b", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 160000, + "output": 16384, + "input": 160000 + }, + "family": "deepseek" + }, + "deepseek/deepseek-r1-turbo": { + "id": "deepseek/deepseek-r1-turbo", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 64000, + "output": 16000 + } + }, + "deepseek/deepseek-ocr-2": { + "id": "deepseek/deepseek-ocr-2", + "reasoning": false, + "toolCall": false, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 8192, + "output": 8192 + } + }, + "deepseek/deepseek-v3.1": { + "id": "deepseek/deepseek-v3.1", + "family": "deepseek", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 163840, + "output": 32768 + } + }, + "deepseek/deepseek-r1-0528": { + "id": "deepseek/deepseek-r1-0528", + "family": "deepseek-thinking", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 163840, + "output": 65536 + } + }, + "deepseek/deepseek-r1-0528-qwen3-8b": { + "id": "deepseek/deepseek-r1-0528-qwen3-8b", + "reasoning": true, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 32000 + } + }, + "deepseek/deepseek-r1-distill-llama-70b": { + "id": "deepseek/deepseek-r1-distill-llama-70b", + "family": "deepseek-thinking", + "reasoning": true, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 131072, + "output": 16384 + } + }, + "deepseek/deepseek-v3-0324": { + "id": "deepseek/deepseek-v3-0324", + "family": "deepseek", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 163840, + "output": 163840 + } + }, + "deepseek/deepseek-ocr": { + "id": "deepseek/deepseek-ocr", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 8192, + "output": 8192 + } + }, + "baidu/ernie-4.5-vl-28b-a3b-thinking": { + "id": "baidu/ernie-4.5-vl-28b-a3b-thinking", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image", + "video" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 131072, + "output": 65536 + } + }, + "baidu/ernie-4.5-vl-424b-a47b": { + "id": "baidu/ernie-4.5-vl-424b-a47b", + "reasoning": true, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "image", + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 123000, + "output": 16000 + }, + "family": "ernie" + }, + "baidu/ernie-4.5-vl-28b-a3b": { + "id": "baidu/ernie-4.5-vl-28b-a3b", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 32768, + "output": 16384, + "input": 32768 + }, + "family": "ernie" + }, + "baidu/ernie-4.5-300b-a47b-paddle": { + "id": "baidu/ernie-4.5-300b-a47b-paddle", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 123000, + "output": 12000 + }, + "family": "ernie" + }, + "baidu/ernie-4.5-21b-a3b": { + "id": "baidu/ernie-4.5-21b-a3b", + "family": "ernie", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 120000, + "output": 8000 + } + }, + "baidu/ernie-4.5-21b-a3b-thinking": { + "id": "baidu/ernie-4.5-21b-a3b-thinking", + "family": "ernie", + "reasoning": true, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 131072, + "output": 65536 + } + }, + "qwen/qwen3-4b-fp8": { + "id": "qwen/qwen3-4b-fp8", + "reasoning": true, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 20000 + } + }, + "qwen/qwen3-32b-fp8": { + "id": "qwen/qwen3-32b-fp8", + "reasoning": true, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 40960, + "output": 20000 + }, + "family": "qwen" + }, + "qwen/qwen3-30b-a3b-fp8": { + "id": "qwen/qwen3-30b-a3b-fp8", + "reasoning": true, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 40960, + "output": 20000 + }, + "family": "qwen" + }, + "qwen/qwen3-coder-next": { + "id": "Qwen/Qwen3-Coder-Next", + "family": "qwen", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 262144, + "output": 65536 + } + }, + "qwen/qwen3-vl-235b-a22b-instruct": { + "id": "Qwen/Qwen3-VL-235B-A22B-Instruct", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 262144, + "output": 262144 + }, + "family": "qwen" + }, + "qwen/qwen-mt-plus": { + "id": "qwen/qwen-mt-plus", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 16384, + "output": 8192 + } + }, + "qwen/qwen3-omni-30b-a3b-instruct": { + "id": "Qwen/Qwen3-Omni-30B-A3B-Instruct", + "family": "qwen", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image", + "audio" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 66000, + "output": 66000 + } + }, + "qwen/qwen-2.5-72b-instruct": { + "id": "qwen/qwen-2.5-72b-instruct", + "family": "qwen", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 32768, + "output": 16384 + } + }, + "qwen/qwen3-vl-30b-a3b-thinking": { + "id": "qwen/qwen3-vl-30b-a3b-thinking", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "image", + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 131072, + "output": 32768 + }, + "family": "qwen" + }, + "qwen/qwen3-vl-235b-a22b-thinking": { + "id": "qwen/qwen3-vl-235b-a22b-thinking", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "image", + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 131072, + "output": 32768 + }, + "family": "qwen" + }, + "qwen/qwen2.5-7b-instruct": { + "id": "Qwen/Qwen2.5-7B-Instruct", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 33000, + "output": 4000 + }, + "family": "qwen" + }, + "qwen/qwen3-235b-a22b-fp8": { + "id": "qwen/qwen3-235b-a22b-fp8", + "reasoning": true, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 40960, + "output": 20000 + }, + "family": "qwen" + }, + "qwen/qwen3-vl-8b-instruct": { + "id": "qwen/qwen3-vl-8b-instruct", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "image", + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 131072, + "output": 32768 + }, + "family": "qwen" + }, + "qwen/qwen3-8b-fp8": { + "id": "qwen/qwen3-8b-fp8", + "reasoning": true, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 20000 + } + }, + "qwen/qwen3-omni-30b-a3b-thinking": { + "id": "Qwen/Qwen3-Omni-30B-A3B-Thinking", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image", + "audio" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 66000, + "output": 66000 + }, + "family": "qwen" + }, + "meta-llama/llama-3-70b-instruct": { + "id": "meta-llama/llama-3-70b-instruct", + "family": "llama", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 8192, + "output": 8000 + } + }, + "meta-llama/llama-3-8b-instruct": { + "id": "meta-llama/llama-3-8b-instruct", + "family": "llama", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 8192, + "output": 16384 + } + }, + "mistralai/mistral-nemo": { + "id": "mistralai/mistral-nemo", + "family": "mistral-nemo", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 131072, + "output": 16384 + } + }, + "sao10k/l3-70b-euryale-v2.1": { + "id": "sao10k/l3-70b-euryale-v2.1", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 8192, + "output": 8192 + } + }, + "sao10k/l31-70b-euryale-v2.2": { + "id": "sao10k/l31-70b-euryale-v2.2", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 8192, + "output": 8192 + } + }, + "sao10k/l3-8b-lunaris": { + "id": "sao10k/l3-8b-lunaris", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 8192, + "output": 8192 + } + }, + "sao10k/l3-8b-stheno-v3.2": { + "id": "Sao10K/L3-8B-Stheno-v3.2", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 16384, + "output": 8192, + "input": 16384 + }, + "family": "llama" + }, + "xiaomimimo/mimo-v2-flash": { + "id": "XiaomiMiMo/MiMo-V2-Flash", + "family": "mimo", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 262144, + "output": 32000 + } + }, + "nousresearch/hermes-2-pro-llama-3-8b": { + "id": "nousresearch/hermes-2-pro-llama-3-8b", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 8192, + "output": 8192 + } + }, + "gpt-5.3-codex": { + "id": "gpt-5.3-codex", + "family": "gpt-codex", + "reasoning": true, + "temperature": false, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 400000, + "input": 272000, + "output": 128000 + } + }, + "gpt-5-codex": { + "id": "gpt-5-codex", + "family": "gpt-codex", + "reasoning": true, + "temperature": false, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 400000, + "input": 272000, + "output": 128000 + } + }, + "gemini-3.1-pro": { + "id": "gemini-3.1-pro", + "family": "gemini-pro", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image", + "video", + "audio", + "pdf" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 1048576, + "output": 65536 + } + }, + "trinity-large-preview-free": { + "id": "trinity-large-preview-free", + "family": "trinity", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 131072, + "output": 131072 + } + }, + "gpt-5.1-codex-max": { + "id": "gpt-5.1-codex-max", + "family": "gpt-codex", + "reasoning": true, + "temperature": false, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 400000, + "input": 272000, + "output": 128000 + } + }, + "kimi-k2.5-free": { + "id": "kimi-k2.5-free", + "family": "kimi-free", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image", + "video" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 262144, + "output": 262144 + } + }, + "claude-opus-4-1": { + "id": "claude-opus-4-1", + "family": "claude-opus", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image", + "pdf" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 200000, + "output": 32000 + } + }, + "grok-code": { + "id": "grok-code", + "family": "grok", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 256000, + "output": 256000 + } + }, + "nemotron-3-super-free": { + "id": "nemotron-3-super-free", + "family": "nemotron-free", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 1000000, + "output": 128000 + } + }, + "claude-3-5-haiku": { + "id": "claude-3-5-haiku", + "family": "claude-haiku", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image", + "pdf" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 200000, + "output": 8192 + } + }, + "gpt-5.2-codex": { + "id": "gpt-5.2-codex", + "family": "gpt-codex", + "reasoning": true, + "temperature": false, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 400000, + "input": 272000, + "output": 128000 + } + }, + "claude-opus-4-6": { + "id": "claude-opus-4-6", + "family": "claude-opus", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image", + "pdf" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 200000, + "output": 128000 + } + }, + "mimo-v2-flash-free": { + "id": "mimo-v2-flash-free", + "family": "mimo-flash-free", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 262144, + "output": 65536 + } + }, + "gemini-3-flash": { + "id": "gemini-3-flash", + "family": "gemini-flash", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image", + "video", + "audio", + "pdf" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 1048576, + "output": 65536 + } + }, + "claude-sonnet-4-6": { + "id": "claude-sonnet-4-6", + "family": "claude-sonnet", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image", + "pdf" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 1000000, + "output": 64000 + } + }, + "gpt-5.1": { + "id": "gpt-5.1", + "family": "gpt", + "reasoning": true, + "temperature": false, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image", + "audio" + ], + "output": [ + "text", + "image", + "audio" + ] + }, + "limit": { + "context": 272000, + "input": 272000, + "output": 128000 + } + }, + "gpt-5.3-codex-spark": { + "id": "gpt-5.3-codex-spark", + "family": "gpt-codex-spark", + "reasoning": true, + "temperature": false, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image", + "pdf" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "input": 100000, + "output": 32000 + } + }, + "qwen3-coder": { + "id": "qwen3-coder", + "family": "qwen", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image", + "audio", + "video" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 262144, + "output": 16384 + } + }, + "gpt-5.1-codex-mini": { + "id": "gpt-5.1-codex-mini", + "family": "gpt-codex", + "reasoning": true, + "temperature": false, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 400000, + "input": 272000, + "output": 128000 + } + }, + "gpt-5.2": { + "id": "gpt-5.2", + "family": "gpt", + "reasoning": true, + "temperature": false, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 400000, + "input": 272000, + "output": 128000 + } + }, + "mimo-v2-omni-free": { + "id": "mimo-v2-omni-free", + "family": "mimo-omni-free", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image", + "audio", + "pdf" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 262144, + "output": 64000 + } + }, + "minimax-m2.1-free": { + "id": "minimax-m2.1-free", + "family": "minimax-free", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 204800, + "output": 131072 + } + }, + "mimo-v2-pro-free": { + "id": "mimo-v2-pro-free", + "family": "mimo-pro-free", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 1048576, + "output": 64000 + } + }, + "gpt-5": { + "id": "gpt-5", + "family": "gpt", + "reasoning": true, + "temperature": false, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 272000, + "input": 272000, + "output": 128000 + } + }, + "glm-5-free": { + "id": "glm-5-free", + "family": "glm-free", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 204800, + "output": 131072 + } + }, + "gpt-5.4": { + "id": "gpt-5.4", + "family": "gpt", + "reasoning": true, + "temperature": false, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image", + "pdf" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 400000, + "input": 272000, + "output": 128000 + } + }, + "gpt-5.4-pro": { + "id": "gpt-5.4-pro", + "family": "gpt-pro", + "reasoning": true, + "temperature": false, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 400000, + "input": 272000, + "output": 128000 + } + }, + "claude-haiku-4-5": { + "id": "claude-haiku-4-5", + "family": "claude-haiku", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image", + "pdf" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 200000, + "output": 200000 + } + }, + "gpt-5.1-codex": { + "id": "gpt-5.1-codex", + "family": "gpt-codex", + "reasoning": true, + "temperature": false, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image", + "audio" + ], + "output": [ + "text", + "image", + "audio" + ] + }, + "limit": { + "context": 400000, + "input": 272000, + "output": 128000 + } + }, + "big-pickle": { + "id": "big-pickle", + "family": "big-pickle", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 200000, + "output": 128000 + } + }, + "minimax-m2.5-free": { + "id": "minimax-m2.5-free", + "family": "minimax-free", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 204800, + "output": 131072 + } + }, + "claude-opus-4-5": { + "id": "claude-opus-4-5", + "family": "claude-opus", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image", + "pdf" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 200000, + "output": 64000 + } + }, + "claude-sonnet-4": { + "id": "claude-sonnet-4", + "family": "claude-sonnet", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image", + "pdf" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 200000, + "output": 64000, + "input": 128000 + } + }, + "glm-4.7-free": { + "id": "glm-4.7-free", + "family": "glm-free", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 204800, + "output": 131072 + } + }, + "gemini-3-pro": { + "id": "gemini-3-pro", + "family": "gemini-pro", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image", + "video", + "audio", + "pdf" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 1048576, + "output": 65536 + } + }, + "claude-sonnet-4-5": { + "id": "claude-sonnet-4-5", + "family": "claude-sonnet", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image", + "pdf" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 200000, + "output": 64000 + } + }, + "gpt-5.4-nano": { + "id": "gpt-5.4-nano", + "family": "gpt-nano", + "reasoning": true, + "temperature": false, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image", + "pdf" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 400000, + "input": 272000, + "output": 128000 + } + }, + "gpt-5-nano": { + "id": "gpt-5-nano", + "family": "gpt-nano", + "reasoning": true, + "temperature": false, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 272000, + "input": 272000, + "output": 128000 + } + }, + "gpt-5.4-mini": { + "id": "gpt-5.4-mini", + "family": "gpt-mini", + "reasoning": true, + "temperature": false, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image", + "pdf" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 400000, + "input": 272000, + "output": 128000 + } + }, + "stabilityai/stablediffusionxl": { + "id": "stabilityai/stablediffusionxl", + "family": "stable-diffusion", + "reasoning": false, + "temperature": false, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "image" + ] + }, + "limit": { + "context": 200, + "output": 0 + } + }, + "ideogramai/ideogram-v2": { + "id": "ideogramai/ideogram-v2", + "family": "ideogram", + "reasoning": false, + "temperature": false, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "image" + ] + }, + "limit": { + "context": 150, + "output": 0 + } + }, + "ideogramai/ideogram": { + "id": "ideogramai/ideogram", + "family": "ideogram", + "reasoning": false, + "temperature": false, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "image" + ] + }, + "limit": { + "context": 150, + "output": 0 + } + }, + "ideogramai/ideogram-v2a-turbo": { + "id": "ideogramai/ideogram-v2a-turbo", + "family": "ideogram", + "reasoning": false, + "temperature": false, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "image" + ] + }, + "limit": { + "context": 150, + "output": 0 + } + }, + "ideogramai/ideogram-v2a": { + "id": "ideogramai/ideogram-v2a", + "family": "ideogram", + "reasoning": false, + "temperature": false, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "image" + ] + }, + "limit": { + "context": 150, + "output": 0 + } + }, + "novita/glm-4.7-flash": { + "id": "novita/glm-4.7-flash", + "reasoning": true, + "temperature": false, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 200000, + "output": 65500 + } + }, + "novita/glm-4.7-n": { + "id": "novita/glm-4.7-n", + "reasoning": true, + "temperature": false, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 205000, + "output": 131072 + } + }, + "novita/glm-4.6": { + "id": "novita/glm-4.6", + "family": "glm", + "reasoning": false, + "temperature": false, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 0, + "output": 0 + } + }, + "novita/minimax-m2.1": { + "id": "novita/minimax-m2.1", + "reasoning": true, + "temperature": false, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 205000, + "output": 131072 + } + }, + "novita/kimi-k2.5": { + "id": "novita/kimi-k2.5", + "reasoning": true, + "temperature": false, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image", + "video" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 256000, + "output": 262144 + } + }, + "novita/glm-4.7": { + "id": "novita/glm-4.7", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 205000, + "output": 131072 + } + }, + "novita/kimi-k2-thinking": { + "id": "novita/kimi-k2-thinking", + "family": "kimi", + "reasoning": true, + "temperature": false, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 256000, + "output": 0 + } + }, + "novita/glm-4.6v": { + "id": "novita/glm-4.6v", + "reasoning": true, + "temperature": false, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 131000, + "output": 32768 + } + }, + "google/gemini-3.1-pro": { + "id": "google/gemini-3.1-pro", + "reasoning": true, + "temperature": false, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image", + "video", + "audio" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 1048576, + "output": 65536 + } + }, + "google/lyria": { + "id": "google/lyria", + "family": "lyria", + "reasoning": false, + "temperature": false, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "audio" + ] + }, + "limit": { + "context": 0, + "output": 0 + } + }, + "google/gemini-3-flash": { + "id": "google/gemini-3-flash", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image", + "pdf" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 1000000, + "output": 64000 + }, + "family": "gemini-flash" + }, + "google/imagen-3": { + "id": "google/imagen-3", + "family": "imagen", + "reasoning": false, + "temperature": false, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "image" + ] + }, + "limit": { + "context": 480, + "output": 0 + } + }, + "google/veo-3.1": { + "id": "google/veo-3.1", + "family": "veo", + "reasoning": false, + "temperature": false, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "video" + ] + }, + "limit": { + "context": 480, + "output": 0 + } + }, + "google/imagen-3-fast": { + "id": "google/imagen-3-fast", + "family": "imagen", + "reasoning": false, + "temperature": false, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "image" + ] + }, + "limit": { + "context": 480, + "output": 0 + } + }, + "google/nano-banana-pro": { + "id": "google/nano-banana-pro", + "family": "nano-banana", + "reasoning": false, + "temperature": false, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "image" + ] + }, + "limit": { + "context": 65536, + "output": 0 + } + }, + "google/veo-2": { + "id": "google/veo-2", + "family": "veo", + "reasoning": false, + "temperature": false, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "video" + ] + }, + "limit": { + "context": 480, + "output": 0 + } + }, + "google/imagen-4-ultra": { + "id": "google/imagen-4-ultra", + "family": "imagen", + "reasoning": false, + "temperature": false, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "image" + ] + }, + "limit": { + "context": 480, + "output": 0 + } + }, + "google/nano-banana": { + "id": "google/nano-banana", + "family": "nano-banana", + "reasoning": false, + "temperature": false, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text", + "image" + ] + }, + "limit": { + "context": 65536, + "output": 0 + } + }, + "google/veo-3.1-fast": { + "id": "google/veo-3.1-fast", + "family": "veo", + "reasoning": false, + "temperature": false, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "video" + ] + }, + "limit": { + "context": 480, + "output": 0 + } + }, + "google/gemini-deep-research": { + "id": "google/gemini-deep-research", + "reasoning": true, + "temperature": false, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image", + "video" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 1048576, + "output": 0 + } + }, + "google/veo-3": { + "id": "google/veo-3", + "family": "veo", + "reasoning": false, + "temperature": false, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "video" + ] + }, + "limit": { + "context": 480, + "output": 0 + } + }, + "google/imagen-4": { + "id": "google/imagen-4", + "family": "imagen", + "reasoning": false, + "temperature": false, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "image" + ] + }, + "limit": { + "context": 480, + "output": 0 + } + }, + "google/gemini-2.0-flash-lite": { + "id": "google/gemini-2.0-flash-lite", + "family": "gemini-flash-lite", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image", + "audio", + "video", + "pdf" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 1048576, + "output": 8192 + } + }, + "google/gemini-3.1-flash-lite": { + "id": "google/gemini-3.1-flash-lite", + "reasoning": true, + "temperature": false, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image", + "video", + "audio" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 1048576, + "output": 65536 + } + }, + "google/gemini-3-pro": { + "id": "google/gemini-3-pro", + "family": "gemini-pro", + "reasoning": true, + "temperature": false, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image", + "video", + "audio" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 1048576, + "output": 65536 + } + }, + "google/gemini-2.0-flash": { + "id": "google/gemini-2.0-flash", + "family": "gemini-flash", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image", + "audio", + "video", + "pdf" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 1048576, + "output": 8192 + } + }, + "google/veo-3-fast": { + "id": "google/veo-3-fast", + "family": "veo", + "reasoning": false, + "temperature": false, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "video" + ] + }, + "limit": { + "context": 480, + "output": 0 + } + }, + "google/imagen-4-fast": { + "id": "google/imagen-4-fast", + "family": "imagen", + "reasoning": false, + "temperature": false, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "image" + ] + }, + "limit": { + "context": 480, + "output": 0 + } + }, + "lumalabs/ray2": { + "id": "lumalabs/ray2", + "family": "ray", + "reasoning": false, + "temperature": false, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "video" + ] + }, + "limit": { + "context": 5000, + "output": 0 + } + }, + "poetools/claude-code": { + "id": "poetools/claude-code", + "reasoning": true, + "temperature": false, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 0, + "output": 0 + } + }, + "openai/gpt-5-pro": { + "id": "openai/gpt-5-pro", + "family": "gpt-pro", + "reasoning": true, + "temperature": false, + "toolCall": false, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 400000, + "output": 128000, + "input": 400000 + } + }, + "openai/gpt-5.1-codex-max": { + "id": "openai/gpt-5.1-codex-max", + "reasoning": true, + "temperature": false, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image", + "pdf" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 400000, + "output": 128000, + "input": 400000 + }, + "family": "gpt-codex" + }, + "openai/o3-deep-research": { + "id": "openai/o3-deep-research", + "family": "o", + "reasoning": true, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 200000, + "output": 100000, + "input": 200000 + } + }, + "openai/o4-mini-deep-research": { + "id": "openai/o4-mini-deep-research", + "family": "o-mini", + "reasoning": true, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 200000, + "output": 100000, + "input": 200000 + } + }, + "openai/gpt-5-chat": { + "id": "openai/gpt-5-chat", + "family": "gpt", + "reasoning": false, + "temperature": false, + "toolCall": false, + "modalities": { + "input": [ + "image", + "pdf", + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 16384, + "input": 111616 + } + }, + "openai/gpt-4-classic": { + "id": "openai/gpt-4-classic", + "family": "gpt", + "reasoning": false, + "temperature": false, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 8192, + "output": 4096 + } + }, + "openai/gpt-5.3-instant": { + "id": "openai/gpt-5.3-instant", + "reasoning": false, + "temperature": false, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "input": 111616, + "output": 16384 + } + }, + "openai/gpt-image-1.5": { + "id": "openai/gpt-image-1.5", + "reasoning": false, + "temperature": false, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "image" + ] + }, + "limit": { + "context": 128000, + "output": 0 + } + }, + "openai/gpt-4.1-nano": { + "id": "openai/gpt-4.1-nano", + "family": "gpt-nano", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text", + "image", + "pdf" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 1047576, + "output": 32768, + "input": 1047576 + } + }, + "openai/gpt-image-1-mini": { + "id": "openai/gpt-image-1-mini", + "family": "gpt", + "reasoning": false, + "temperature": false, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "image" + ] + }, + "limit": { + "context": 0, + "output": 0 + } + }, + "openai/sora-2-pro": { + "id": "openai/sora-2-pro", + "family": "sora", + "reasoning": false, + "temperature": false, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "video" + ] + }, + "limit": { + "context": 0, + "output": 0 + } + }, + "openai/gpt-4o-aug": { + "id": "openai/gpt-4o-aug", + "family": "gpt", + "reasoning": false, + "temperature": false, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 8192 + } + }, + "openai/gpt-image-1": { + "id": "openai/gpt-image-1", + "family": "gpt", + "reasoning": false, + "temperature": false, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "image" + ] + }, + "limit": { + "context": 128000, + "output": 0 + } + }, + "openai/sora-2": { + "id": "openai/sora-2", + "family": "sora", + "reasoning": false, + "temperature": false, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "video" + ] + }, + "limit": { + "context": 0, + "output": 0 + } + }, + "openai/gpt-3.5-turbo-raw": { + "id": "openai/gpt-3.5-turbo-raw", + "family": "gpt", + "reasoning": false, + "temperature": false, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 4524, + "output": 2048 + } + }, + "openai/gpt-4o-mini-search": { + "id": "openai/gpt-4o-mini-search", + "family": "gpt-mini", + "reasoning": false, + "temperature": false, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 8192 + } + }, + "openai/gpt-4.1-mini": { + "id": "openai/gpt-4.1-mini", + "family": "gpt-mini", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 1047576, + "output": 32768, + "input": 1047576 + } + }, + "openai/o1-pro": { + "id": "openai/o1-pro", + "family": "o-pro", + "reasoning": false, + "temperature": false, + "toolCall": false, + "modalities": { + "input": [ + "text", + "image", + "pdf" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 200000, + "output": 100000, + "input": 200000 + } + }, + "openai/chatgpt-4o-latest": { + "id": "openai/chatgpt-4o-latest", + "family": "gpt", + "reasoning": false, + "temperature": false, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 16384, + "input": 128000 + } + }, + "openai/dall-e-3": { + "id": "openai/dall-e-3", + "family": "dall-e", + "reasoning": false, + "temperature": false, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "image" + ] + }, + "limit": { + "context": 800, + "output": 0 + } + }, + "openai/gpt-4o-search": { + "id": "openai/gpt-4o-search", + "family": "gpt", + "reasoning": false, + "temperature": false, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 8192 + } + }, + "openai/gpt-4-classic-0314": { + "id": "openai/gpt-4-classic-0314", + "family": "gpt", + "reasoning": false, + "temperature": false, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 8192, + "output": 4096 + } + }, + "openai/gpt-3.5-turbo-instruct": { + "id": "openai/gpt-3.5-turbo-instruct", + "family": "gpt", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 4095, + "output": 4096, + "input": 4096 + } + }, + "openai/gpt-5.2-instant": { + "id": "openai/gpt-5.2-instant", + "reasoning": false, + "temperature": false, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 16384 + } + }, + "openai/o3-mini-high": { + "id": "openai/o3-mini-high", + "family": "o-mini", + "reasoning": true, + "temperature": false, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 200000, + "output": 100000, + "input": 200000 + } + }, + "openai/gpt-5.1-instant": { + "id": "openai/gpt-5.1-instant", + "family": "gpt", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image", + "pdf" + ], + "output": [ + "text", + "image" + ] + }, + "limit": { + "context": 128000, + "output": 16384, + "input": 111616 + } + }, + "topazlabs-co/topazlabs": { + "id": "topazlabs-co/topazlabs", + "family": "topazlabs", + "reasoning": false, + "temperature": false, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "image" + ] + }, + "limit": { + "context": 204, + "output": 0 + } + }, + "runwayml/runway": { + "id": "runwayml/runway", + "family": "runway", + "reasoning": false, + "temperature": false, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "video" + ] + }, + "limit": { + "context": 256, + "output": 0 + } + }, + "runwayml/runway-gen-4-turbo": { + "id": "runwayml/runway-gen-4-turbo", + "family": "runway", + "reasoning": false, + "temperature": false, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "video" + ] + }, + "limit": { + "context": 256, + "output": 0 + } + }, + "anthropic/claude-sonnet-3.5-june": { + "id": "anthropic/claude-sonnet-3.5-june", + "family": "claude-sonnet", + "reasoning": false, + "temperature": false, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image", + "pdf" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 189096, + "output": 8192 + } + }, + "anthropic/claude-sonnet-3.5": { + "id": "anthropic/claude-sonnet-3.5", + "family": "claude-sonnet", + "reasoning": false, + "temperature": false, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image", + "pdf" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 189096, + "output": 8192 + } + }, + "anthropic/claude-haiku-3": { + "id": "anthropic/claude-haiku-3", + "family": "claude-haiku", + "reasoning": false, + "temperature": false, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image", + "pdf" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 189096, + "output": 8192 + } + }, + "anthropic/claude-haiku-3.5": { + "id": "anthropic/claude-haiku-3.5", + "family": "claude-haiku", + "reasoning": false, + "temperature": false, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image", + "pdf" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 189096, + "output": 8192 + } + }, + "anthropic/claude-sonnet-3.7": { + "id": "anthropic/claude-sonnet-3.7", + "family": "claude-sonnet", + "reasoning": true, + "temperature": false, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image", + "pdf" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 196608, + "output": 128000 + } + }, + "trytako/tako": { + "id": "trytako/tako", + "family": "tako", + "reasoning": false, + "temperature": false, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 2048, + "output": 0 + } + }, + "elevenlabs/elevenlabs-music": { + "id": "elevenlabs/elevenlabs-music", + "family": "elevenlabs", + "reasoning": false, + "temperature": false, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "audio" + ] + }, + "limit": { + "context": 2000, + "output": 0 + } + }, + "elevenlabs/elevenlabs-v3": { + "id": "elevenlabs/elevenlabs-v3", + "family": "elevenlabs", + "reasoning": false, + "temperature": false, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "audio" + ] + }, + "limit": { + "context": 128000, + "output": 0 + } + }, + "elevenlabs/elevenlabs-v2.5-turbo": { + "id": "elevenlabs/elevenlabs-v2.5-turbo", + "family": "elevenlabs", + "reasoning": false, + "temperature": false, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "audio" + ] + }, + "limit": { + "context": 128000, + "output": 0 + } + }, + "cerebras/llama-3.1-8b-cs": { + "id": "cerebras/llama-3.1-8b-cs", + "reasoning": false, + "temperature": false, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 0, + "output": 0 + } + }, + "cerebras/gpt-oss-120b-cs": { + "id": "cerebras/gpt-oss-120b-cs", + "reasoning": true, + "temperature": false, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 0, + "output": 0 + } + }, + "cerebras/qwen3-235b-2507-cs": { + "id": "cerebras/qwen3-235b-2507-cs", + "reasoning": true, + "temperature": false, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 0, + "output": 0 + } + }, + "cerebras/llama-3.3-70b-cs": { + "id": "cerebras/llama-3.3-70b-cs", + "reasoning": false, + "temperature": false, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 0, + "output": 0 + } + }, + "cerebras/qwen3-32b-cs": { + "id": "cerebras/qwen3-32b-cs", + "reasoning": true, + "temperature": false, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 0, + "output": 0 + } + }, + "xai/grok-4-fast-reasoning": { + "id": "xai/grok-4-fast-reasoning", + "family": "grok", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 2000000, + "output": 256000 + } + }, + "xai/grok-3": { + "id": "xai/grok-3", + "family": "grok", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 131072, + "output": 8192 + } + }, + "xai/grok-code-fast-1": { + "id": "xai/grok-code-fast-1", + "family": "grok", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 256000, + "output": 10000 + } + }, + "xai/grok-4.1-fast-reasoning": { + "id": "xai/grok-4.1-fast-reasoning", + "family": "grok", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 2000000, + "output": 30000 + } + }, + "xai/grok-4": { + "id": "xai/grok-4", + "family": "grok", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 256000, + "output": 64000 + } + }, + "xai/grok-4.1-fast-non-reasoning": { + "id": "xai/grok-4.1-fast-non-reasoning", + "family": "grok", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 2000000, + "output": 30000 + } + }, + "xai/grok-3-mini": { + "id": "xai/grok-3-mini", + "family": "grok", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 131072, + "output": 8192 + } + }, + "xai/grok-4-fast-non-reasoning": { + "id": "xai/grok-4-fast-non-reasoning", + "family": "grok", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 2000000, + "output": 30000 + } + }, + "deepseek.r1-v1:0": { + "id": "deepseek.r1-v1:0", + "family": "deepseek-thinking", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 32768 + } + }, + "meta.llama3-1-70b-instruct-v1:0": { + "id": "meta.llama3-1-70b-instruct-v1:0", + "family": "llama", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 4096 + } + }, + "qwen.qwen3-coder-480b-a35b-v1:0": { + "id": "qwen.qwen3-coder-480b-a35b-v1:0", + "family": "qwen", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 131072, + "output": 65536 + } + }, + "eu.anthropic.claude-sonnet-4-6": { + "id": "eu.anthropic.claude-sonnet-4-6", + "family": "claude-sonnet", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image", + "pdf" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 1000000, + "output": 64000 + } + }, + "eu.anthropic.claude-haiku-4-5-20251001-v1:0": { + "id": "eu.anthropic.claude-haiku-4-5-20251001-v1:0", + "family": "claude-haiku", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image", + "pdf" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 200000, + "output": 64000 + } + }, + "mistral.mistral-large-3-675b-instruct": { + "id": "mistral.mistral-large-3-675b-instruct", + "family": "mistral", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 256000, + "output": 8192 + } + }, + "openai.gpt-oss-120b-1:0": { + "id": "openai.gpt-oss-120b-1:0", + "family": "gpt-oss", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 4096 + } + }, + "us.anthropic.claude-opus-4-20250514-v1:0": { + "id": "us.anthropic.claude-opus-4-20250514-v1:0", + "family": "claude-opus", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image", + "pdf" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 200000, + "output": 32000 + } + }, + "nvidia.nemotron-nano-12b-v2": { + "id": "nvidia.nemotron-nano-12b-v2", + "family": "nemotron", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 4096 + } + }, + "anthropic.claude-3-7-sonnet-20250219-v1:0": { + "id": "anthropic.claude-3-7-sonnet-20250219-v1:0", + "family": "claude-sonnet", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image", + "pdf" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 200000, + "output": 8192 + } + }, + "anthropic.claude-sonnet-4-6": { + "id": "anthropic.claude-sonnet-4-6", + "family": "claude-sonnet", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image", + "pdf" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 1000000, + "output": 64000 + } + }, + "minimax.minimax-m2.1": { + "id": "minimax.minimax-m2.1", + "family": "minimax", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 204800, + "output": 131072 + } + }, + "global.anthropic.claude-opus-4-5-20251101-v1:0": { + "id": "global.anthropic.claude-opus-4-5-20251101-v1:0", + "family": "claude-opus", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image", + "pdf" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 200000, + "output": 64000 + } + }, + "mistral.ministral-3-8b-instruct": { + "id": "mistral.ministral-3-8b-instruct", + "family": "ministral", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 4096 + } + }, + "openai.gpt-oss-safeguard-20b": { + "id": "openai.gpt-oss-safeguard-20b", + "family": "gpt-oss", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 4096 + } + }, + "amazon.nova-lite-v1:0": { + "id": "amazon.nova-lite-v1:0", + "family": "nova-lite", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image", + "video" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 300000, + "output": 8192 + } + }, + "eu.anthropic.claude-sonnet-4-5-20250929-v1:0": { + "id": "eu.anthropic.claude-sonnet-4-5-20250929-v1:0", + "family": "claude-sonnet", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image", + "pdf" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 200000, + "output": 64000 + } + }, + "mistral.pixtral-large-2502-v1:0": { + "id": "mistral.pixtral-large-2502-v1:0", + "family": "mistral", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 8192 + } + }, + "google.gemma-3-12b-it": { + "id": "google.gemma-3-12b-it", + "family": "gemma", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 131072, + "output": 8192 + } + }, + "meta.llama3-1-8b-instruct-v1:0": { + "id": "meta.llama3-1-8b-instruct-v1:0", + "family": "llama", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 4096 + } + }, + "mistral.devstral-2-123b": { + "id": "mistral.devstral-2-123b", + "family": "devstral", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 256000, + "output": 8192 + } + }, + "anthropic.claude-sonnet-4-5-20250929-v1:0": { + "id": "anthropic.claude-sonnet-4-5-20250929-v1:0", + "family": "claude-sonnet", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image", + "pdf" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 200000, + "output": 64000 + } + }, + "meta.llama4-maverick-17b-instruct-v1:0": { + "id": "meta.llama4-maverick-17b-instruct-v1:0", + "family": "llama", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 1000000, + "output": 16384 + } + }, + "mistral.ministral-3-14b-instruct": { + "id": "mistral.ministral-3-14b-instruct", + "family": "ministral", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 4096 + } + }, + "minimax.minimax-m2": { + "id": "minimax.minimax-m2", + "family": "minimax", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 204608, + "output": 128000 + } + }, + "amazon.nova-micro-v1:0": { + "id": "amazon.nova-micro-v1:0", + "family": "nova-micro", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 8192 + } + }, + "anthropic.claude-3-5-sonnet-20241022-v2:0": { + "id": "anthropic.claude-3-5-sonnet-20241022-v2:0", + "family": "claude-sonnet", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image", + "pdf" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 200000, + "output": 8192 + } + }, + "nvidia.nemotron-nano-3-30b": { + "id": "nvidia.nemotron-nano-3-30b", + "family": "nemotron", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 4096 + } + }, + "anthropic.claude-sonnet-4-20250514-v1:0": { + "id": "anthropic.claude-sonnet-4-20250514-v1:0", + "family": "claude-sonnet", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image", + "pdf" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 200000, + "output": 64000 + } + }, + "qwen.qwen3-vl-235b-a22b": { + "id": "qwen.qwen3-vl-235b-a22b", + "family": "qwen", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 262000, + "output": 262000 + } + }, + "global.anthropic.claude-opus-4-6-v1": { + "id": "global.anthropic.claude-opus-4-6-v1", + "family": "claude-opus", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image", + "pdf" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 1000000, + "output": 128000 + } + }, + "writer.palmyra-x4-v1:0": { + "id": "writer.palmyra-x4-v1:0", + "family": "palmyra", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 122880, + "output": 8192 + } + }, + "minimax.minimax-m2.5": { + "id": "minimax.minimax-m2.5", + "family": "minimax-m2.5", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 1000000, + "output": 131072 + } + }, + "amazon.nova-pro-v1:0": { + "id": "amazon.nova-pro-v1:0", + "family": "nova-pro", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image", + "video" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 300000, + "output": 8192 + } + }, + "us.anthropic.claude-opus-4-5-20251101-v1:0": { + "id": "us.anthropic.claude-opus-4-5-20251101-v1:0", + "family": "claude-opus", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image", + "pdf" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 200000, + "output": 64000 + } + }, + "meta.llama3-2-90b-instruct-v1:0": { + "id": "meta.llama3-2-90b-instruct-v1:0", + "family": "llama", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 4096 + } + }, + "us.anthropic.claude-opus-4-6-v1": { + "id": "us.anthropic.claude-opus-4-6-v1", + "family": "claude-opus", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image", + "pdf" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 1000000, + "output": 128000 + } + }, + "google.gemma-3-4b-it": { + "id": "google.gemma-3-4b-it", + "family": "gemma", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 4096 + } + }, + "anthropic.claude-opus-4-6-v1": { + "id": "anthropic.claude-opus-4-6-v1", + "family": "claude-opus", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image", + "pdf" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 1000000, + "output": 128000 + } + }, + "zai.glm-4.7-flash": { + "id": "zai.glm-4.7-flash", + "family": "glm-flash", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 200000, + "output": 131072 + } + }, + "anthropic.claude-opus-4-20250514-v1:0": { + "id": "anthropic.claude-opus-4-20250514-v1:0", + "family": "claude-opus", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image", + "pdf" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 200000, + "output": 32000 + } + }, + "global.anthropic.claude-sonnet-4-6": { + "id": "global.anthropic.claude-sonnet-4-6", + "family": "claude-sonnet", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image", + "pdf" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 1000000, + "output": 64000 + } + }, + "meta.llama3-2-1b-instruct-v1:0": { + "id": "meta.llama3-2-1b-instruct-v1:0", + "family": "llama", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 131000, + "output": 4096 + } + }, + "anthropic.claude-opus-4-1-20250805-v1:0": { + "id": "anthropic.claude-opus-4-1-20250805-v1:0", + "family": "claude-opus", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image", + "pdf" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 200000, + "output": 32000 + } + }, + "meta.llama4-scout-17b-instruct-v1:0": { + "id": "meta.llama4-scout-17b-instruct-v1:0", + "family": "llama", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 3500000, + "output": 16384 + } + }, + "deepseek.v3.2": { + "id": "deepseek.v3.2", + "family": "deepseek", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 163840, + "output": 81920 + } + }, + "deepseek.v3-v1:0": { + "id": "deepseek.v3-v1:0", + "family": "deepseek", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 163840, + "output": 81920 + } + }, + "mistral.ministral-3-3b-instruct": { + "id": "mistral.ministral-3-3b-instruct", + "family": "ministral", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 256000, + "output": 8192 + } + }, + "global.anthropic.claude-haiku-4-5-20251001-v1:0": { + "id": "global.anthropic.claude-haiku-4-5-20251001-v1:0", + "family": "claude-haiku", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image", + "pdf" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 200000, + "output": 64000 + } + }, + "nvidia.nemotron-nano-9b-v2": { + "id": "nvidia.nemotron-nano-9b-v2", + "family": "nemotron", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 4096 + } + }, + "writer.palmyra-x5-v1:0": { + "id": "writer.palmyra-x5-v1:0", + "family": "palmyra", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 1040000, + "output": 8192 + } + }, + "meta.llama3-3-70b-instruct-v1:0": { + "id": "meta.llama3-3-70b-instruct-v1:0", + "family": "llama", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 4096 + } + }, + "zai.glm-4.7": { + "id": "zai.glm-4.7", + "family": "glm", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 204800, + "output": 131072 + } + }, + "moonshot.kimi-k2-thinking": { + "id": "moonshot.kimi-k2-thinking", + "family": "kimi-thinking", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 256000, + "output": 256000 + } + }, + "anthropic.claude-3-haiku-20240307-v1:0": { + "id": "anthropic.claude-3-haiku-20240307-v1:0", + "family": "claude-haiku", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image", + "pdf" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 200000, + "output": 4096 + } + }, + "us.anthropic.claude-sonnet-4-5-20250929-v1:0": { + "id": "us.anthropic.claude-sonnet-4-5-20250929-v1:0", + "family": "claude-sonnet", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image", + "pdf" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 200000, + "output": 64000 + } + }, + "openai.gpt-oss-20b-1:0": { + "id": "openai.gpt-oss-20b-1:0", + "family": "gpt-oss", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 4096 + } + }, + "us.anthropic.claude-sonnet-4-6": { + "id": "us.anthropic.claude-sonnet-4-6", + "family": "claude-sonnet", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image", + "pdf" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 1000000, + "output": 64000 + } + }, + "meta.llama3-2-11b-instruct-v1:0": { + "id": "meta.llama3-2-11b-instruct-v1:0", + "family": "llama", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 4096 + } + }, + "eu.anthropic.claude-opus-4-5-20251101-v1:0": { + "id": "eu.anthropic.claude-opus-4-5-20251101-v1:0", + "family": "claude-opus", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image", + "pdf" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 200000, + "output": 64000 + } + }, + "meta.llama3-1-405b-instruct-v1:0": { + "id": "meta.llama3-1-405b-instruct-v1:0", + "family": "llama", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 4096 + } + }, + "qwen.qwen3-next-80b-a3b": { + "id": "qwen.qwen3-next-80b-a3b", + "family": "qwen", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 262000, + "output": 262000 + } + }, + "us.anthropic.claude-sonnet-4-20250514-v1:0": { + "id": "us.anthropic.claude-sonnet-4-20250514-v1:0", + "family": "claude-sonnet", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image", + "pdf" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 200000, + "output": 64000 + } + }, + "qwen.qwen3-coder-30b-a3b-v1:0": { + "id": "qwen.qwen3-coder-30b-a3b-v1:0", + "family": "qwen", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 262144, + "output": 131072 + } + }, + "us.anthropic.claude-haiku-4-5-20251001-v1:0": { + "id": "us.anthropic.claude-haiku-4-5-20251001-v1:0", + "family": "claude-haiku", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image", + "pdf" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 200000, + "output": 64000 + } + }, + "qwen.qwen3-235b-a22b-2507-v1:0": { + "id": "qwen.qwen3-235b-a22b-2507-v1:0", + "family": "qwen", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 262144, + "output": 131072 + } + }, + "openai.gpt-oss-safeguard-120b": { + "id": "openai.gpt-oss-safeguard-120b", + "family": "gpt-oss", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 4096 + } + }, + "anthropic.claude-3-5-sonnet-20240620-v1:0": { + "id": "anthropic.claude-3-5-sonnet-20240620-v1:0", + "family": "claude-sonnet", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image", + "pdf" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 200000, + "output": 8192 + } + }, + "mistral.voxtral-small-24b-2507": { + "id": "mistral.voxtral-small-24b-2507", + "family": "mistral", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "audio" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 32000, + "output": 8192 + } + }, + "anthropic.claude-haiku-4-5-20251001-v1:0": { + "id": "anthropic.claude-haiku-4-5-20251001-v1:0", + "family": "claude-haiku", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image", + "pdf" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 200000, + "output": 64000 + } + }, + "meta.llama3-2-3b-instruct-v1:0": { + "id": "meta.llama3-2-3b-instruct-v1:0", + "family": "llama", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 131000, + "output": 4096 + } + }, + "google.gemma-3-27b-it": { + "id": "google.gemma-3-27b-it", + "family": "gemma", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 202752, + "output": 8192 + } + }, + "us.anthropic.claude-opus-4-1-20250805-v1:0": { + "id": "us.anthropic.claude-opus-4-1-20250805-v1:0", + "family": "claude-opus", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image", + "pdf" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 200000, + "output": 32000 + } + }, + "global.anthropic.claude-sonnet-4-20250514-v1:0": { + "id": "global.anthropic.claude-sonnet-4-20250514-v1:0", + "family": "claude-sonnet", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image", + "pdf" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 200000, + "output": 64000 + } + }, + "anthropic.claude-3-5-haiku-20241022-v1:0": { + "id": "anthropic.claude-3-5-haiku-20241022-v1:0", + "family": "claude-haiku", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image", + "pdf" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 200000, + "output": 8192 + } + }, + "zai.glm-5": { + "id": "zai.glm-5", + "family": "glm", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 200000, + "output": 131072 + } + }, + "eu.anthropic.claude-sonnet-4-20250514-v1:0": { + "id": "eu.anthropic.claude-sonnet-4-20250514-v1:0", + "family": "claude-sonnet", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image", + "pdf" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 200000, + "output": 64000 + } + }, + "anthropic.claude-opus-4-5-20251101-v1:0": { + "id": "anthropic.claude-opus-4-5-20251101-v1:0", + "family": "claude-opus", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image", + "pdf" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 200000, + "output": 64000 + } + }, + "eu.anthropic.claude-opus-4-6-v1": { + "id": "eu.anthropic.claude-opus-4-6-v1", + "family": "claude-opus", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image", + "pdf" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 1000000, + "output": 128000 + } + }, + "amazon.nova-premier-v1:0": { + "id": "amazon.nova-premier-v1:0", + "family": "nova", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image", + "video" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 1000000, + "output": 16384 + } + }, + "amazon.nova-2-lite-v1:0": { + "id": "amazon.nova-2-lite-v1:0", + "family": "nova", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image", + "video" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 4096 + } + }, + "qwen.qwen3-32b-v1:0": { + "id": "qwen.qwen3-32b-v1:0", + "family": "qwen", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 16384, + "output": 16384 + } + }, + "mistral.magistral-small-2509": { + "id": "mistral.magistral-small-2509", + "family": "magistral", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 40000 + } + }, + "moonshotai.kimi-k2.5": { + "id": "moonshotai.kimi-k2.5", + "family": "kimi", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 256000, + "output": 256000 + } + }, + "mistral.voxtral-mini-3b-2507": { + "id": "mistral.voxtral-mini-3b-2507", + "family": "mistral", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "audio", + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 4096 + } + }, + "global.anthropic.claude-sonnet-4-5-20250929-v1:0": { + "id": "global.anthropic.claude-sonnet-4-5-20250929-v1:0", + "family": "claude-sonnet", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image", + "pdf" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 200000, + "output": 64000 + } + }, + "ring-1t": { + "id": "Ring-1T", + "family": "ring", + "reasoning": true, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 32000 + } + }, + "ling-1t": { + "id": "Ling-1T", + "family": "ling", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 32000 + } + }, + "phi-3-small-8k-instruct": { + "id": "phi-3-small-8k-instruct", + "family": "phi", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 8192, + "output": 2048 + } + }, + "gpt-4o": { + "id": "gpt-4o", + "family": "gpt", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 16384, + "input": 64000 + } + }, + "codestral-2501": { + "id": "codestral-2501", + "family": "codestral", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 256000, + "output": 256000 + } + }, + "mistral-small-2503": { + "id": "mistral-small-2503", + "family": "mistral-small", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 32768 + } + }, + "o1-mini": { + "id": "o1-mini", + "family": "o-mini", + "reasoning": true, + "temperature": false, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 65536 + } + }, + "gpt-3.5-turbo-instruct": { + "id": "gpt-3.5-turbo-instruct", + "family": "gpt", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 4096, + "output": 4096 + } + }, + "gpt-4": { + "id": "gpt-4", + "family": "gpt", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 8192, + "output": 8192 + } + }, + "gpt-3.5-turbo-1106": { + "id": "gpt-3.5-turbo-1106", + "family": "gpt", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 16384, + "output": 16384 + } + }, + "phi-4-reasoning": { + "id": "phi-4-reasoning", + "family": "phi", + "reasoning": true, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 32000, + "output": 4096 + } + }, + "phi-3-mini-128k-instruct": { + "id": "phi-3-mini-128k-instruct", + "family": "phi", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 4096 + } + }, + "gpt-5-mini": { + "id": "gpt-5-mini", + "family": "gpt-mini", + "reasoning": true, + "temperature": false, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 272000, + "output": 128000, + "input": 272000 + } + }, + "grok-4-fast-non-reasoning": { + "id": "grok-4-fast-non-reasoning", + "family": "grok", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 2000000, + "output": 30000 + } + }, + "o3-mini": { + "id": "o3-mini", + "family": "o-mini", + "reasoning": true, + "temperature": false, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 200000, + "output": 100000 + } + }, + "cohere-embed-v3-english": { + "id": "cohere-embed-v3-english", + "family": "cohere-embed", + "reasoning": false, + "temperature": false, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 512, + "output": 1024 + } + }, + "phi-3-medium-4k-instruct": { + "id": "phi-3-medium-4k-instruct", + "family": "phi", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 4096, + "output": 1024 + } + }, + "cohere-embed-v3-multilingual": { + "id": "cohere-embed-v3-multilingual", + "family": "cohere-embed", + "reasoning": false, + "temperature": false, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 512, + "output": 1024 + } + }, + "gpt-3.5-turbo-0125": { + "id": "gpt-3.5-turbo-0125", + "family": "gpt", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 16384, + "output": 16384 + } + }, + "phi-4-mini-reasoning": { + "id": "phi-4-mini-reasoning", + "family": "phi", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 4096 + } + }, + "mistral-large-2411": { + "id": "mistral-large-2411", + "family": "mistral-large", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 32768 + } + }, + "meta-llama-3.1-8b-instruct": { + "id": "meta-llama-3.1-8b-instruct", + "family": "llama", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 32768 + } + }, + "o1-preview": { + "id": "o1-preview", + "family": "o", + "reasoning": true, + "temperature": false, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 32768 + } + }, + "meta-llama-3.1-70b-instruct": { + "id": "meta-llama-3.1-70b-instruct", + "family": "llama", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 32768 + } + }, + "phi-3-mini-4k-instruct": { + "id": "phi-3-mini-4k-instruct", + "family": "phi", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 4096, + "output": 1024 + } + }, + "codex-mini": { + "id": "codex-mini", + "family": "gpt-codex-mini", + "reasoning": true, + "temperature": false, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 200000, + "output": 100000 + } + }, + "phi-4-reasoning-plus": { + "id": "phi-4-reasoning-plus", + "family": "phi", + "reasoning": true, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 32000, + "output": 4096 + } + }, + "gpt-4.1-mini": { + "id": "gpt-4.1-mini", + "family": "gpt-mini", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 1047576, + "output": 32768 + } + }, + "phi-4": { + "id": "phi-4", + "family": "phi", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 4096 + } + }, + "o4-mini": { + "id": "o4-mini", + "family": "o-mini", + "reasoning": true, + "temperature": false, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 200000, + "output": 100000 + } + }, + "gpt-4-32k": { + "id": "gpt-4-32k", + "family": "gpt", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 32768, + "output": 32768 + } + }, + "grok-3-mini": { + "id": "grok-3-mini", + "family": "grok", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 131072, + "output": 8192 + } + }, + "cohere-embed-v-4-0": { + "id": "cohere-embed-v-4-0", + "family": "cohere-embed", + "reasoning": false, + "temperature": false, + "toolCall": false, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 1536 + } + }, + "mistral-nemo": { + "id": "mistral-nemo", + "family": "mistral-nemo", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 128000 + } + }, + "gpt-4-turbo": { + "id": "gpt-4-turbo", + "family": "gpt", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 4096 + } + }, + "gpt-4.1": { + "id": "gpt-4.1", + "family": "gpt", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 1047576, + "output": 32768, + "input": 64000 + } + }, + "model-router": { + "id": "model-router", + "family": "model-router", + "reasoning": false, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 16384 + } + }, + "text-embedding-3-large": { + "id": "text-embedding-3-large", + "family": "text-embedding", + "reasoning": false, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 8191, + "output": 3072 + }, + "temperature": false + }, + "gpt-3.5-turbo-0613": { + "id": "gpt-3.5-turbo-0613", + "family": "gpt", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 16384, + "output": 16384 + } + }, + "cohere-command-r-08-2024": { + "id": "cohere-command-r-08-2024", + "family": "command-r", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 4000 + } + }, + "gpt-4.1-nano": { + "id": "gpt-4.1-nano", + "family": "gpt-nano", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 1047576, + "output": 32768 + } + }, + "deepseek-v3.2-speciale": { + "id": "deepseek-v3.2-speciale", + "family": "deepseek", + "reasoning": true, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 128000 + } + }, + "phi-4-mini": { + "id": "phi-4-mini", + "family": "phi", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 4096 + } + }, + "text-embedding-3-small": { + "id": "text-embedding-3-small", + "family": "text-embedding", + "reasoning": false, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 8191, + "output": 1536 + }, + "temperature": false + }, + "gpt-3.5-turbo-0301": { + "id": "gpt-3.5-turbo-0301", + "family": "gpt", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 4096, + "output": 4096 + } + }, + "meta-llama-3-70b-instruct": { + "id": "meta-llama-3-70b-instruct", + "family": "llama", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 8192, + "output": 2048 + } + }, + "llama-3.2-11b-vision-instruct": { + "id": "llama-3.2-11b-vision-instruct", + "family": "llama", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 8192 + } + }, + "o3": { + "id": "o3", + "family": "o", + "reasoning": true, + "temperature": false, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 200000, + "output": 100000 + } + }, + "meta-llama-3-8b-instruct": { + "id": "meta-llama-3-8b-instruct", + "family": "llama", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 8192, + "output": 2048 + } + }, + "gpt-5.1-chat": { + "id": "gpt-5.1-chat", + "family": "gpt-codex", + "reasoning": true, + "temperature": false, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image", + "audio" + ], + "output": [ + "text", + "image", + "audio" + ] + }, + "limit": { + "context": 128000, + "output": 16384 + } + }, + "grok-4": { + "id": "grok-4", + "family": "grok", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 256000, + "output": 64000 + } + }, + "gpt-5-chat": { + "id": "gpt-5-chat", + "family": "gpt-codex", + "reasoning": true, + "temperature": false, + "toolCall": false, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 16384 + } + }, + "gpt-5.2-chat": { + "id": "gpt-5.2-chat", + "family": "gpt-codex", + "reasoning": true, + "temperature": false, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 16384 + } + }, + "cohere-command-r-plus-08-2024": { + "id": "cohere-command-r-plus-08-2024", + "family": "command-r", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 4000 + } + }, + "meta-llama-3.1-405b-instruct": { + "id": "meta-llama-3.1-405b-instruct", + "family": "llama", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 32768 + } + }, + "llama-4-scout-17b-16e-instruct": { + "id": "llama-4-scout-17b-16e-instruct", + "family": "llama", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 8192 + } + }, + "o1": { + "id": "o1", + "family": "o", + "reasoning": true, + "temperature": false, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 200000, + "output": 100000 + } + }, + "mistral-medium-2505": { + "id": "mistral-medium-2505", + "family": "mistral-medium", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 128000 + } + }, + "cohere-command-a": { + "id": "cohere-command-a", + "family": "command-a", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 256000, + "output": 8000 + } + }, + "phi-3.5-mini-instruct": { + "id": "phi-3.5-mini-instruct", + "family": "phi", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 4096 + } + }, + "grok-code-fast-1": { + "id": "grok-code-fast-1", + "family": "grok", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 256000, + "output": 10000, + "input": 128000 + } + }, + "llama-3.2-90b-vision-instruct": { + "id": "llama-3.2-90b-vision-instruct", + "family": "llama", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 8192 + } + }, + "grok-3": { + "id": "grok-3", + "family": "grok", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 131072, + "output": 8192 + } + }, + "ministral-3b": { + "id": "ministral-3b", + "family": "ministral", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 8192 + } + }, + "gpt-4-turbo-vision": { + "id": "gpt-4-turbo-vision", + "family": "gpt", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 4096 + } + }, + "phi-3.5-moe-instruct": { + "id": "phi-3.5-moe-instruct", + "family": "phi", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 4096 + } + }, + "mai-ds-r1": { + "id": "mai-ds-r1", + "family": "mai", + "reasoning": true, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 8192 + } + }, + "phi-4-multimodal": { + "id": "phi-4-multimodal", + "family": "phi", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text", + "image", + "audio" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 4096 + } + }, + "phi-3-medium-128k-instruct": { + "id": "phi-3-medium-128k-instruct", + "family": "phi", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 4096 + } + }, + "grok-4-fast-reasoning": { + "id": "grok-4-fast-reasoning", + "family": "grok", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 2000000, + "output": 30000 + } + }, + "text-embedding-ada-002": { + "id": "text-embedding-ada-002", + "family": "text-embedding", + "reasoning": false, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 8192, + "output": 1536 + }, + "temperature": false + }, + "gpt-4o-mini": { + "id": "gpt-4o-mini", + "family": "gpt-mini", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 16384 + } + }, + "phi-3-small-128k-instruct": { + "id": "phi-3-small-128k-instruct", + "family": "phi", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 4096 + } + }, + "gpt-5-pro": { + "id": "gpt-5-pro", + "family": "gpt-pro", + "reasoning": true, + "temperature": false, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 400000, + "output": 272000, + "input": 272000 + } + }, + "qwen-vl-plus": { + "id": "qwen-vl-plus", + "family": "qwen", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 131072, + "output": 8192 + } + }, + "qwen-vl-max": { + "id": "qwen-vl-max", + "family": "qwen", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 131072, + "output": 8192 + } + }, + "qwen3-14b": { + "id": "qwen3-14b", + "family": "qwen", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 131072, + "output": 8192 + } + }, + "qwen3-coder-flash": { + "id": "qwen3-coder-flash", + "family": "qwen", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 1000000, + "output": 65536 + } + }, + "qwen3-vl-30b-a3b": { + "id": "qwen3-vl-30b-a3b", + "family": "qwen", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 131072, + "output": 32768 + } + }, + "qwen3-asr-flash": { + "id": "qwen3-asr-flash", + "family": "qwen", + "reasoning": false, + "temperature": false, + "toolCall": false, + "modalities": { + "input": [ + "audio" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 53248, + "output": 4096 + } + }, + "qwen-max": { + "id": "qwen-max", + "family": "qwen", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 131072, + "output": 8192, + "input": 32000 + } + }, + "qwen2-5-7b-instruct": { + "id": "qwen2-5-7b-instruct", + "family": "qwen", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 131072, + "output": 8192 + } + }, + "qwen2-5-vl-72b-instruct": { + "id": "qwen2-5-vl-72b-instruct", + "family": "qwen", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 131072, + "output": 8192 + } + }, + "qwen2-5-14b-instruct": { + "id": "qwen2-5-14b-instruct", + "family": "qwen", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 131072, + "output": 8192 + } + }, + "qwen3-8b": { + "id": "qwen3-8b", + "family": "qwen", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 131072, + "output": 8192 + } + }, + "qvq-max": { + "id": "qvq-max", + "family": "qvq", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 131072, + "output": 8192, + "input": 128000 + } + }, + "qwen2-5-omni-7b": { + "id": "qwen2-5-omni-7b", + "family": "qwen", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image", + "audio", + "video" + ], + "output": [ + "text", + "audio" + ] + }, + "limit": { + "context": 32768, + "output": 2048 + } + }, + "qwen2-5-vl-7b-instruct": { + "id": "qwen2-5-vl-7b-instruct", + "family": "qwen", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 131072, + "output": 8192 + } + }, + "qwen-omni-turbo-realtime": { + "id": "qwen-omni-turbo-realtime", + "family": "qwen", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image", + "audio" + ], + "output": [ + "text", + "audio" + ] + }, + "limit": { + "context": 32768, + "output": 2048 + } + }, + "qwen-omni-turbo": { + "id": "qwen-omni-turbo", + "family": "qwen", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image", + "audio", + "video" + ], + "output": [ + "text", + "audio" + ] + }, + "limit": { + "context": 32768, + "output": 2048 + } + }, + "qwen-mt-plus": { + "id": "qwen-mt-plus", + "family": "qwen", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 16384, + "output": 8192 + } + }, + "qwen3-livetranslate-flash-realtime": { + "id": "qwen3-livetranslate-flash-realtime", + "family": "qwen", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text", + "image", + "audio", + "video" + ], + "output": [ + "text", + "audio" + ] + }, + "limit": { + "context": 53248, + "output": 4096 + } + }, + "qwen-plus": { + "id": "qwen-plus", + "family": "qwen", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 1000000, + "output": 32768, + "input": 995904 + } + }, + "qwen2-5-32b-instruct": { + "id": "qwen2-5-32b-instruct", + "family": "qwen", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 131072, + "output": 8192 + } + }, + "qwen3-omni-flash": { + "id": "qwen3-omni-flash", + "family": "qwen", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image", + "audio", + "video" + ], + "output": [ + "text", + "audio" + ] + }, + "limit": { + "context": 65536, + "output": 16384 + } + }, + "qwen-flash": { + "id": "qwen-flash", + "family": "qwen", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 1000000, + "output": 32768 + } + }, + "qwen2-5-72b-instruct": { + "id": "qwen2-5-72b-instruct", + "family": "qwen", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 131072, + "output": 8192 + } + }, + "qwen3-omni-flash-realtime": { + "id": "qwen3-omni-flash-realtime", + "family": "qwen", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image", + "audio" + ], + "output": [ + "text", + "audio" + ] + }, + "limit": { + "context": 65536, + "output": 16384 + } + }, + "qwen-vl-ocr": { + "id": "qwen-vl-ocr", + "family": "qwen", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 34096, + "output": 4096 + } + }, + "qwq-plus": { + "id": "qwq-plus", + "family": "qwen", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 131072, + "output": 8192 + } + }, + "qwen3-vl-235b-a22b": { + "id": "qwen3-vl-235b-a22b", + "family": "qwen", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 131072, + "output": 32768 + } + }, + "qwen-plus-character-ja": { + "id": "qwen-plus-character-ja", + "family": "qwen", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 8192, + "output": 512 + } + }, + "qwen-mt-turbo": { + "id": "qwen-mt-turbo", + "family": "qwen", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 16384, + "output": 8192 + } + }, + "@cf/zai-org/glm-4.7-flash": { + "id": "@cf/zai-org/glm-4.7-flash", + "family": "glm-flash", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 131072, + "output": 131072 + } + }, + "@cf/nvidia/nemotron-3-120b-a12b": { + "id": "@cf/nvidia/nemotron-3-120b-a12b", + "family": "nemotron", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 256000, + "output": 256000 + } + }, + "@cf/ibm-granite/granite-4.0-h-micro": { + "id": "@cf/ibm-granite/granite-4.0-h-micro", + "family": "granite", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 16384 + } + }, + "@cf/baai/bge-small-en-v1.5": { + "id": "@cf/baai/bge-small-en-v1.5", + "family": "bge", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 16384 + } + }, + "@cf/baai/bge-large-en-v1.5": { + "id": "@cf/baai/bge-large-en-v1.5", + "family": "bge", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 16384 + } + }, + "@cf/baai/bge-reranker-base": { + "id": "@cf/baai/bge-reranker-base", + "family": "bge", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 16384 + } + }, + "@cf/baai/bge-m3": { + "id": "@cf/baai/bge-m3", + "family": "bge", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 16384 + } + }, + "@cf/baai/bge-base-en-v1.5": { + "id": "@cf/baai/bge-base-en-v1.5", + "family": "bge", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 16384 + } + }, + "@cf/pfnet/plamo-embedding-1b": { + "id": "@cf/pfnet/plamo-embedding-1b", + "family": "plamo", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 16384 + } + }, + "@cf/deepseek-ai/deepseek-r1-distill-qwen-32b": { + "id": "@cf/deepseek-ai/deepseek-r1-distill-qwen-32b", + "family": "deepseek-thinking", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 16384 + } + }, + "@cf/facebook/bart-large-cnn": { + "id": "@cf/facebook/bart-large-cnn", + "family": "bart", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 16384 + } + }, + "@cf/mistral/mistral-7b-instruct-v0.1": { + "id": "@cf/mistral/mistral-7b-instruct-v0.1", + "family": "mistral", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 16384 + } + }, + "@cf/myshell-ai/melotts": { + "id": "@cf/myshell-ai/melotts", + "family": "melotts", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 16384 + } + }, + "@cf/pipecat-ai/smart-turn-v2": { + "id": "@cf/pipecat-ai/smart-turn-v2", + "family": "smart-turn", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 16384 + } + }, + "@cf/moonshotai/kimi-k2.5": { + "id": "@cf/moonshotai/kimi-k2.5", + "family": "kimi", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 256000, + "output": 256000 + } + }, + "@cf/google/gemma-3-12b-it": { + "id": "@cf/google/gemma-3-12b-it", + "family": "gemma", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 16384 + } + }, + "@cf/qwen/qwq-32b": { + "id": "@cf/qwen/qwq-32b", + "family": "qwen", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 16384 + } + }, + "@cf/qwen/qwen3-30b-a3b-fp8": { + "id": "@cf/qwen/qwen3-30b-a3b-fp8", + "family": "qwen", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 16384 + } + }, + "@cf/qwen/qwen2.5-coder-32b-instruct": { + "id": "@cf/qwen/qwen2.5-coder-32b-instruct", + "family": "qwen", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 16384 + } + }, + "@cf/qwen/qwen3-embedding-0.6b": { + "id": "@cf/qwen/qwen3-embedding-0.6b", + "family": "qwen", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 16384 + } + }, + "@cf/meta/llama-3.1-8b-instruct-fp8": { + "id": "@cf/meta/llama-3.1-8b-instruct-fp8", + "family": "llama", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 16384 + } + }, + "@cf/meta/llama-3-8b-instruct-awq": { + "id": "@cf/meta/llama-3-8b-instruct-awq", + "family": "llama", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 16384 + } + }, + "@cf/meta/llama-3.1-8b-instruct-awq": { + "id": "@cf/meta/llama-3.1-8b-instruct-awq", + "family": "llama", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 16384 + } + }, + "@cf/meta/llama-4-scout-17b-16e-instruct": { + "id": "@cf/meta/llama-4-scout-17b-16e-instruct", + "family": "llama", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 16384 + } + }, + "@cf/meta/llama-3.2-11b-vision-instruct": { + "id": "@cf/meta/llama-3.2-11b-vision-instruct", + "family": "llama", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 16384 + } + }, + "@cf/meta/llama-3.2-3b-instruct": { + "id": "@cf/meta/llama-3.2-3b-instruct", + "family": "llama", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 16384 + } + }, + "@cf/meta/llama-guard-3-8b": { + "id": "@cf/meta/llama-guard-3-8b", + "family": "llama", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 16384 + } + }, + "@cf/meta/llama-3.2-1b-instruct": { + "id": "@cf/meta/llama-3.2-1b-instruct", + "family": "llama", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 16384 + } + }, + "@cf/meta/llama-3.3-70b-instruct-fp8-fast": { + "id": "@cf/meta/llama-3.3-70b-instruct-fp8-fast", + "family": "llama", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 16384 + } + }, + "@cf/meta/llama-3.1-8b-instruct": { + "id": "@cf/meta/llama-3.1-8b-instruct", + "family": "llama", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 16384 + } + }, + "@cf/meta/m2m100-1.2b": { + "id": "@cf/meta/m2m100-1.2b", + "family": "m2m", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 16384 + } + }, + "@cf/meta/llama-2-7b-chat-fp16": { + "id": "@cf/meta/llama-2-7b-chat-fp16", + "family": "llama", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 16384 + } + }, + "@cf/meta/llama-3-8b-instruct": { + "id": "@cf/meta/llama-3-8b-instruct", + "family": "llama", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 16384 + } + }, + "@cf/mistralai/mistral-small-3.1-24b-instruct": { + "id": "@cf/mistralai/mistral-small-3.1-24b-instruct", + "family": "mistral-small", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 16384 + } + }, + "@cf/deepgram/aura-2-es": { + "id": "@cf/deepgram/aura-2-es", + "family": "aura", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 16384 + } + }, + "@cf/deepgram/nova-3": { + "id": "@cf/deepgram/nova-3", + "family": "nova", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 16384 + } + }, + "@cf/deepgram/aura-2-en": { + "id": "@cf/deepgram/aura-2-en", + "family": "aura", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 16384 + } + }, + "@cf/openai/gpt-oss-120b": { + "id": "@cf/openai/gpt-oss-120b", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 16384 + } + }, + "@cf/openai/gpt-oss-20b": { + "id": "@cf/openai/gpt-oss-20b", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 16384 + } + }, + "@cf/ai4bharat/indictrans2-en-indic-1b": { + "id": "@cf/ai4bharat/indictrans2-en-indic-1B", + "family": "indictrans", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 16384 + } + }, + "@cf/huggingface/distilbert-sst-2-int8": { + "id": "@cf/huggingface/distilbert-sst-2-int8", + "family": "distilbert", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 16384 + } + }, + "@cf/aisingapore/gemma-sea-lion-v4-27b-it": { + "id": "@cf/aisingapore/gemma-sea-lion-v4-27b-it", + "family": "gemma", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 16384 + } + }, + "llama3-70b-8192": { + "id": "llama3-70b-8192", + "family": "llama", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 8192, + "output": 8192 + } + }, + "qwen-qwq-32b": { + "id": "qwen-qwq-32b", + "family": "qwen", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 131072, + "output": 16384 + } + }, + "llama-3.1-8b-instant": { + "id": "llama-3.1-8b-instant", + "family": "llama", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 131072, + "output": 32678 + } + }, + "llama-guard-3-8b": { + "id": "llama-guard-3-8b", + "family": "llama", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 8192, + "output": 8192 + } + }, + "llama3-8b-8192": { + "id": "llama3-8b-8192", + "family": "llama", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 8192, + "output": 8192 + } + }, + "mistral-saba-24b": { + "id": "mistral-saba-24b", + "family": "mistral", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 32768, + "output": 32768 + } + }, + "llama-3.3-70b-versatile": { + "id": "llama-3.3-70b-versatile", + "family": "llama", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 131072, + "output": 32678 + } + }, + "gemma2-9b-it": { + "id": "gemma2-9b-it", + "family": "gemma", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 8192, + "output": 8192 + } + }, + "meta-llama/llama-guard-4-12b": { + "id": "meta-llama/llama-guard-4-12b", + "family": "llama", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "image", + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 163840, + "output": 32768 + } + }, + "meta-llama/llama-4-maverick-17b-128e-instruct": { + "id": "meta-llama/llama-4-maverick-17b-128e-instruct", + "family": "llama", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 131072, + "output": 8192 + } + }, + "zai-org/glm-5-fp8": { + "id": "zai-org/GLM-5-FP8", + "family": "glm", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 200000, + "output": 200000 + } + }, + "nvidia/nvidia-nemotron-3-super-120b-a12b-fp8": { + "id": "nvidia/NVIDIA-Nemotron-3-Super-120B-A12B-FP8", + "family": "nemotron", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 262144, + "output": 262144 + } + }, + "openpipe/qwen3-14b-instruct": { + "id": "OpenPipe/Qwen3-14B-Instruct", + "family": "qwen", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 32768, + "output": 32768 + } + }, + "coding-glm-4.7-free": { + "id": "coding-glm-4.7-free", + "family": "glm", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 204800, + "output": 131072 + } + }, + "coding-minimax-m2.1-free": { + "id": "coding-minimax-m2.1-free", + "family": "minimax", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 204800, + "output": 131072 + } + }, + "claude-opus-4-6-think": { + "id": "claude-opus-4-6-think", + "family": "claude-opus", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image", + "pdf" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 200000, + "output": 128000 + } + }, + "gemini-3-pro-preview-search": { + "id": "gemini-3-pro-preview-search", + "family": "gemini-pro", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image", + "audio", + "video" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 1000000, + "output": 65000 + } + }, + "deepseek-v3.2-think": { + "id": "deepseek-v3.2-think", + "family": "deepseek", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 131000, + "output": 64000 + } + }, + "gemini-3-pro-preview": { + "id": "gemini-3-pro-preview", + "family": "gemini-pro", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 1048756, + "output": 65536, + "input": 1048756 + } + }, + "deepseek-v3.2-fast": { + "id": "deepseek-v3.2-fast", + "family": "deepseek", + "reasoning": false, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 128000 + } + }, + "coding-glm-4.7": { + "id": "coding-glm-4.7", + "family": "glm", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 204800, + "output": 131072 + } + }, + "coding-glm-5-free": { + "id": "coding-glm-5-free", + "family": "glm", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 204800, + "output": 131072 + } + }, + "claude-sonnet-4-6-think": { + "id": "claude-sonnet-4-6-think", + "family": "claude-sonnet", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image", + "pdf" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 200000, + "output": 64000 + } + }, + "k2p5": { + "id": "k2p5", + "family": "kimi-thinking", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image", + "video" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 262144, + "output": 32768 + } + }, + "devstral-medium-2507": { + "id": "devstral-medium-2507", + "family": "devstral", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 128000 + } + }, + "labs-devstral-small-2512": { + "id": "labs-devstral-small-2512", + "family": "devstral", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 256000, + "output": 256000 + } + }, + "devstral-medium-latest": { + "id": "devstral-medium-latest", + "family": "devstral", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 262144, + "output": 262144 + } + }, + "open-mistral-7b": { + "id": "open-mistral-7b", + "family": "mistral", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 8000, + "output": 8000 + } + }, + "mistral-small-2506": { + "id": "mistral-small-2506", + "family": "mistral-small", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 16384 + } + }, + "codestral-latest": { + "id": "codestral-latest", + "family": "codestral", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 256000, + "output": 4096 + } + }, + "ministral-8b-latest": { + "id": "ministral-8b-latest", + "family": "ministral", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 128000 + } + }, + "magistral-small": { + "id": "magistral-small", + "family": "magistral-small", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 128000 + } + }, + "mistral-large-2512": { + "id": "mistral-large-2512", + "family": "mistral-large", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 262144 + } + }, + "ministral-3b-latest": { + "id": "ministral-3b-latest", + "family": "ministral", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 128000 + } + }, + "mistral-embed": { + "id": "mistral-embed", + "family": "mistral-embed", + "reasoning": false, + "temperature": false, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 8000, + "output": 3072 + } + }, + "devstral-small-2505": { + "id": "devstral-small-2505", + "family": "devstral", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 128000 + } + }, + "pixtral-12b": { + "id": "pixtral-12b", + "family": "pixtral", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 128000 + } + }, + "open-mixtral-8x7b": { + "id": "open-mixtral-8x7b", + "family": "mixtral", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 32000, + "output": 32000 + } + }, + "pixtral-large-latest": { + "id": "pixtral-large-latest", + "family": "pixtral", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 128000 + } + }, + "devstral-2512": { + "id": "devstral-2512", + "family": "devstral", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 262000, + "output": 262000 + } + }, + "mistral-large-latest": { + "id": "mistral-large-latest", + "family": "mistral-large", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 262144, + "output": 262144 + } + }, + "mistral-medium-2508": { + "id": "mistral-medium-2508", + "family": "mistral-medium", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 262144, + "output": 262144 + } + }, + "mistral-small-latest": { + "id": "mistral-small-latest", + "family": "mistral-small", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 16384 + } + }, + "open-mixtral-8x22b": { + "id": "open-mixtral-8x22b", + "family": "mixtral", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 64000, + "output": 64000 + } + }, + "mistral-medium-latest": { + "id": "mistral-medium-latest", + "family": "mistral-medium", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 16384 + } + }, + "devstral-small-2507": { + "id": "devstral-small-2507", + "family": "devstral", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 128000 + } + }, + "magistral-medium-latest": { + "id": "magistral-medium-latest", + "family": "magistral-medium", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 16384 + } + }, + "gpt-4o-2024-11-20": { + "id": "gpt-4o-2024-11-20", + "family": "gpt", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 16384 + } + }, + "claude-opus-4-5-20251101": { + "id": "claude-opus-4-5-20251101", + "family": "claude-opus", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image", + "pdf" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 200000, + "output": 32000, + "input": 200000 + } + }, + "gpt-5.2-chat-latest": { + "id": "gpt-5.2-chat-latest", + "family": "gpt-codex", + "reasoning": true, + "temperature": false, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 16384 + } + }, + "grok-4-0709": { + "id": "grok-4-0709", + "family": "grok", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 256000, + "output": 8192 + } + }, + "gpt-5.3-codex-xhigh": { + "id": "gpt-5.3-codex-xhigh", + "family": "gpt", + "reasoning": true, + "temperature": false, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image", + "pdf" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 400000, + "input": 272000, + "output": 128000 + } + }, + "grok-4-1-fast-non-reasoning": { + "id": "grok-4-1-fast-non-reasoning", + "family": "grok", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 2000000, + "output": 30000, + "input": 128000 + } + }, + "gemini-3.1-flash-lite-preview": { + "id": "gemini-3.1-flash-lite-preview", + "family": "gemini-flash-lite", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image", + "video", + "audio", + "pdf" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 1048576, + "output": 65536 + } + }, + "claude-opus-4-20250514": { + "id": "claude-opus-4-20250514", + "family": "claude-opus", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image", + "pdf" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 200000, + "output": 32000, + "input": 200000 + } + }, + "claude-sonnet-4-5-20250929": { + "id": "claude-sonnet-4-5-20250929", + "family": "claude-sonnet", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image", + "pdf" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 1000000, + "output": 64000, + "input": 1000000 + } + }, + "o3-pro": { + "id": "o3-pro", + "family": "o-pro", + "reasoning": true, + "temperature": false, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 200000, + "output": 100000 + } + }, + "gemini-3.1-pro-preview": { + "id": "gemini-3.1-pro-preview", + "family": "gemini-pro", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image", + "video", + "audio", + "pdf" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 1048576, + "output": 65536, + "input": 128000 + } + }, + "claude-3-7-sonnet-20250219": { + "id": "claude-3-7-sonnet-20250219", + "family": "claude-sonnet", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image", + "pdf" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 200000, + "output": 16000, + "input": 200000 + } + }, + "claude-haiku-4-5-20251001": { + "id": "claude-haiku-4-5-20251001", + "family": "claude-haiku", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image", + "pdf" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 200000, + "output": 64000, + "input": 200000 + } + }, + "kimi-k2-turbo-preview": { + "id": "kimi-k2-turbo-preview", + "family": "kimi", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 262144, + "output": 262144 + } + }, + "qwen-2.5-coder-32b": { + "id": "qwen-2.5-coder-32b", + "family": "qwen", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 8192 + } + }, + "route-llm": { + "id": "route-llm", + "family": "gpt", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 16384 + } + }, + "gpt-5.3-chat-latest": { + "id": "gpt-5.3-chat-latest", + "family": "gpt", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 400000, + "output": 128000 + } + }, + "claude-sonnet-4-20250514": { + "id": "claude-sonnet-4-20250514", + "family": "claude-sonnet", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image", + "pdf" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 200000, + "output": 64000, + "input": 200000 + } + }, + "gpt-5.1-chat-latest": { + "id": "gpt-5.1-chat-latest", + "family": "gpt-codex", + "reasoning": true, + "temperature": false, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 16384 + } + }, + "claude-opus-4-1-20250805": { + "id": "claude-opus-4-1-20250805", + "family": "claude-opus", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image", + "pdf" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 200000, + "output": 32000, + "input": 200000 + } + }, + "meta-llama/meta-llama-3.1-405b-instruct-turbo": { + "id": "meta-llama/Meta-Llama-3.1-405B-Instruct-Turbo", + "family": "llama", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 4096 + } + }, + "qwen/qwen2.5-72b-instruct": { + "id": "Qwen/Qwen2.5-72B-Instruct", + "family": "qwen", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 32768, + "output": 32768 + } + }, + "accounts/fireworks/routers/kimi-k2p5-turbo": { + "id": "accounts/fireworks/routers/kimi-k2p5-turbo", + "family": "kimi-thinking", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 256000, + "output": 256000 + } + }, + "accounts/fireworks/models/kimi-k2-instruct": { + "id": "accounts/fireworks/models/kimi-k2-instruct", + "family": "kimi", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 16384 + } + }, + "accounts/fireworks/models/glm-4p7": { + "id": "accounts/fireworks/models/glm-4p7", + "family": "glm", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 198000, + "output": 198000 + } + }, + "accounts/fireworks/models/glm-5": { + "id": "accounts/fireworks/models/glm-5", + "family": "glm", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 202752, + "output": 131072 + } + }, + "accounts/fireworks/models/deepseek-v3p1": { + "id": "accounts/fireworks/models/deepseek-v3p1", + "family": "deepseek", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 163840, + "output": 163840 + } + }, + "accounts/fireworks/models/minimax-m2p1": { + "id": "accounts/fireworks/models/minimax-m2p1", + "family": "minimax", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 200000, + "output": 200000 + } + }, + "accounts/fireworks/models/glm-4p5-air": { + "id": "accounts/fireworks/models/glm-4p5-air", + "family": "glm-air", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 131072, + "output": 131072 + } + }, + "accounts/fireworks/models/deepseek-v3p2": { + "id": "accounts/fireworks/models/deepseek-v3p2", + "family": "deepseek", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 160000, + "output": 160000 + } + }, + "accounts/fireworks/models/minimax-m2p5": { + "id": "accounts/fireworks/models/minimax-m2p5", + "family": "minimax", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 196608, + "output": 196608 + } + }, + "accounts/fireworks/models/gpt-oss-120b": { + "id": "accounts/fireworks/models/gpt-oss-120b", + "family": "gpt-oss", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 131072, + "output": 32768 + } + }, + "accounts/fireworks/models/kimi-k2p5": { + "id": "accounts/fireworks/models/kimi-k2p5", + "family": "kimi-thinking", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image", + "video" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 256000, + "output": 256000 + } + }, + "accounts/fireworks/models/kimi-k2-thinking": { + "id": "accounts/fireworks/models/kimi-k2-thinking", + "family": "kimi-thinking", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 256000, + "output": 256000 + } + }, + "accounts/fireworks/models/glm-4p5": { + "id": "accounts/fireworks/models/glm-4p5", + "family": "glm", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 131072, + "output": 131072 + } + }, + "accounts/fireworks/models/gpt-oss-20b": { + "id": "accounts/fireworks/models/gpt-oss-20b", + "family": "gpt-oss", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 131072, + "output": 32768 + } + }, + "step-3.5-flash": { + "id": "step-3.5-flash", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 256000, + "input": 256000, + "output": 256000 + } + }, + "step-2-16k": { + "id": "step-2-16k", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 16384, + "input": 16384, + "output": 8192 + } + }, + "step-1-32k": { + "id": "step-1-32k", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 32768, + "input": 32768, + "output": 32768 + } + }, + "duo-chat-gpt-5-2-codex": { + "id": "duo-chat-gpt-5-2-codex", + "family": "gpt-codex", + "reasoning": true, + "temperature": false, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image", + "pdf" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 400000, + "input": 272000, + "output": 128000 + } + }, + "duo-chat-opus-4-6": { + "id": "duo-chat-opus-4-6", + "family": "claude-opus", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image", + "pdf" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 1000000, + "output": 64000 + } + }, + "duo-chat-gpt-5-mini": { + "id": "duo-chat-gpt-5-mini", + "family": "gpt-mini", + "reasoning": true, + "temperature": false, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 400000, + "input": 272000, + "output": 128000 + } + }, + "duo-chat-gpt-5-3-codex": { + "id": "duo-chat-gpt-5-3-codex", + "family": "gpt-codex", + "reasoning": true, + "temperature": false, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image", + "pdf" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 400000, + "input": 272000, + "output": 128000 + } + }, + "duo-chat-sonnet-4-5": { + "id": "duo-chat-sonnet-4-5", + "family": "claude-sonnet", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image", + "pdf" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 200000, + "output": 64000 + } + }, + "duo-chat-haiku-4-5": { + "id": "duo-chat-haiku-4-5", + "family": "claude-haiku", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image", + "pdf" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 200000, + "output": 64000 + } + }, + "duo-chat-gpt-5-codex": { + "id": "duo-chat-gpt-5-codex", + "family": "gpt-codex", + "reasoning": true, + "temperature": false, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 400000, + "input": 272000, + "output": 128000 + } + }, + "duo-chat-gpt-5-4-nano": { + "id": "duo-chat-gpt-5-4-nano", + "family": "gpt-nano", + "reasoning": true, + "temperature": false, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 400000, + "input": 272000, + "output": 128000 + } + }, + "duo-chat-gpt-5-2": { + "id": "duo-chat-gpt-5-2", + "family": "gpt", + "reasoning": true, + "temperature": false, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 400000, + "input": 272000, + "output": 128000 + } + }, + "duo-chat-gpt-5-4-mini": { + "id": "duo-chat-gpt-5-4-mini", + "family": "gpt-mini", + "reasoning": true, + "temperature": false, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 400000, + "input": 272000, + "output": 128000 + } + }, + "duo-chat-sonnet-4-6": { + "id": "duo-chat-sonnet-4-6", + "family": "claude-sonnet", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image", + "pdf" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 1000000, + "output": 64000 + } + }, + "duo-chat-gpt-5-4": { + "id": "duo-chat-gpt-5-4", + "family": "gpt", + "reasoning": true, + "temperature": false, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image", + "pdf" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 1050000, + "input": 922000, + "output": 128000 + } + }, + "duo-chat-opus-4-5": { + "id": "duo-chat-opus-4-5", + "family": "claude-opus", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image", + "pdf" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 200000, + "output": 64000 + } + }, + "duo-chat-gpt-5-1": { + "id": "duo-chat-gpt-5-1", + "family": "gpt", + "reasoning": true, + "temperature": false, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 400000, + "input": 272000, + "output": 128000 + } + }, + "nex-agi/deepseek-v3.1-nex-n1": { + "id": "nex-agi/deepseek-v3.1-nex-n1", + "family": "deepseek", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 8192, + "input": 128000 + } + }, + "deepseek-ai/deepseek-r1-distill-qwen-32b": { + "id": "deepseek-ai/DeepSeek-R1-Distill-Qwen-32B", + "family": "qwen", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 131000, + "output": 131000 + } + }, + "deepseek-ai/deepseek-r1-distill-qwen-14b": { + "id": "deepseek-ai/DeepSeek-R1-Distill-Qwen-14B", + "family": "qwen", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 131000, + "output": 131000 + } + }, + "deepseek-ai/deepseek-v3.2-exp": { + "id": "deepseek-ai/deepseek-v3.2-exp", + "family": "deepseek", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 163840, + "output": 65536, + "input": 163840 + } + }, + "deepseek-ai/deepseek-vl2": { + "id": "deepseek-ai/deepseek-vl2", + "family": "deepseek", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 4000, + "output": 4000 + } + }, + "deepseek-ai/deepseek-v3": { + "id": "deepseek-ai/DeepSeek-V3", + "family": "deepseek", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 163840, + "output": 163840 + } + }, + "bytedance-seed/seed-oss-36b-instruct": { + "id": "ByteDance-Seed/Seed-OSS-36B-Instruct", + "family": "seed", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 262000, + "output": 262000 + } + }, + "tencent/hunyuan-a13b-instruct": { + "id": "tencent/hunyuan-a13b-instruct", + "family": "hunyuan", + "reasoning": true, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 131072, + "output": 131072 + } + }, + "tencent/hunyuan-mt-7b": { + "id": "tencent/Hunyuan-MT-7B", + "family": "hunyuan", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 8192, + "output": 8192, + "input": 8192 + } + }, + "inclusionai/ling-flash-2.0": { + "id": "inclusionAI/Ling-flash-2.0", + "family": "ling", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 131000, + "output": 131000 + } + }, + "inclusionai/ring-flash-2.0": { + "id": "inclusionAI/Ring-flash-2.0", + "family": "ring", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 131000, + "output": 131000 + } + }, + "inclusionai/ling-mini-2.0": { + "id": "inclusionAI/Ling-mini-2.0", + "family": "ling", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 131000, + "output": 131000 + } + }, + "baidu/ernie-4.5-300b-a47b": { + "id": "baidu/ernie-4.5-300b-a47b", + "family": "ernie", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 131072, + "output": 16384, + "input": 131072 + } + }, + "qwen/qwen3-vl-32b-instruct": { + "id": "qwen/qwen3-vl-32b-instruct", + "family": "qwen", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 131072, + "output": 32768 + } + }, + "qwen/qwen2.5-vl-7b-instruct": { + "id": "Qwen/Qwen2.5-VL-7B-Instruct", + "family": "qwen", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 33000, + "output": 4000 + } + }, + "qwen/qwen2.5-32b-instruct": { + "id": "Qwen/Qwen2.5-32B-Instruct", + "family": "qwen", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 33000, + "output": 4000 + } + }, + "qwen/qwen3-8b": { + "id": "qwen/qwen3-8b", + "family": "qwen", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 40960, + "output": 8192 + } + }, + "qwen/qwen2.5-14b-instruct": { + "id": "Qwen/Qwen2.5-14B-Instruct", + "family": "qwen", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 33000, + "output": 4000 + } + }, + "qwen/qwen2.5-72b-instruct-128k": { + "id": "Qwen/Qwen2.5-72B-Instruct-128K", + "family": "qwen", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 131000, + "output": 4000 + } + }, + "qwen/qwen3-omni-30b-a3b-captioner": { + "id": "Qwen/Qwen3-Omni-30B-A3B-Captioner", + "family": "qwen", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "audio" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 66000, + "output": 66000 + } + }, + "qwen/qwen3-vl-8b-thinking": { + "id": "qwen/qwen3-vl-8b-thinking", + "family": "qwen", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "image", + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 131072, + "output": 32768 + } + }, + "qwen/qwen3-vl-32b-thinking": { + "id": "Qwen/Qwen3-VL-32B-Thinking", + "family": "qwen", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 262000, + "output": 262000 + } + }, + "qwen/qwen3-14b": { + "id": "Qwen/Qwen3-14B", + "family": "qwen", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 40960, + "output": 40960 + } + }, + "thudm/glm-4-32b-0414": { + "id": "THUDM/GLM-4-32B-0414", + "family": "glm", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 65536, + "input": 128000 + } + }, + "thudm/glm-4-9b-0414": { + "id": "THUDM/GLM-4-9B-0414", + "family": "glm", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 32000, + "output": 8000, + "input": 32000 + } + }, + "thudm/glm-z1-32b-0414": { + "id": "THUDM/GLM-Z1-32B-0414", + "family": "glm-z", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 65536, + "input": 128000 + } + }, + "thudm/glm-z1-9b-0414": { + "id": "THUDM/GLM-Z1-9B-0414", + "family": "glm-z", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 32000, + "output": 8000, + "input": 32000 + } + }, + "essentialai/rnj-1-instruct": { + "id": "essentialai/rnj-1-instruct", + "family": "rnj", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 8192, + "input": 128000 + } + }, + "deepseek-ai/deepseek-v3-1": { + "id": "deepseek-ai/DeepSeek-V3-1", + "family": "deepseek", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 131072, + "output": 131072 + } + }, + "qwen/qwen3-235b-a22b-instruct-2507-tput": { + "id": "Qwen/Qwen3-235B-A22B-Instruct-2507-tput", + "family": "qwen", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 262144, + "output": 262144 + } + }, + "qwen/qwen3-coder-next-fp8": { + "id": "Qwen/Qwen3-Coder-Next-FP8", + "family": "qwen", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 262144, + "output": 262144 + } + }, + "minimaxai/chat-completion/models/minimax-m2_5-high-throughput": { + "id": "minimaxai/chat-completion/models/MiniMax-M2_5-high-throughput", + "family": "minimax", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 204800, + "output": 131072 + } + }, + "arcee_ai/afm/models/trinity-mini": { + "id": "arcee_ai/AFM/models/trinity-mini", + "family": "trinity-mini", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 131072, + "output": 131072 + } + }, + "deepseek-ai/deepseek-ocr/models/deepseek-ocr": { + "id": "deepseek-ai/deepseek-ocr/models/DeepSeek-OCR", + "family": "deepseek", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 8192, + "output": 8192 + } + }, + "clarifai/main/models/mm-poly-8b": { + "id": "clarifai/main/models/mm-poly-8b", + "family": "mm-poly", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text", + "image", + "video" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 32768, + "output": 4096 + } + }, + "qwen/qwencoder/models/qwen3-coder-30b-a3b-instruct": { + "id": "qwen/qwenCoder/models/Qwen3-Coder-30B-A3B-Instruct", + "family": "qwen", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 262144, + "output": 65536 + } + }, + "qwen/qwenlm/models/qwen3-30b-a3b-instruct-2507": { + "id": "qwen/qwenLM/models/Qwen3-30B-A3B-Instruct-2507", + "family": "qwen", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 262144, + "output": 262144 + } + }, + "qwen/qwenlm/models/qwen3-30b-a3b-thinking-2507": { + "id": "qwen/qwenLM/models/Qwen3-30B-A3B-Thinking-2507", + "family": "qwen", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 262144, + "output": 131072 + } + }, + "mistralai/completion/models/ministral-3-14b-reasoning-2512": { + "id": "mistralai/completion/models/Ministral-3-14B-Reasoning-2512", + "family": "ministral", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 262144, + "output": 262144 + } + }, + "mistralai/completion/models/ministral-3-3b-reasoning-2512": { + "id": "mistralai/completion/models/Ministral-3-3B-Reasoning-2512", + "family": "ministral", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 262144, + "output": 262144 + } + }, + "openai/chat-completion/models/gpt-oss-120b-high-throughput": { + "id": "openai/chat-completion/models/gpt-oss-120b-high-throughput", + "family": "gpt-oss", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 131072, + "output": 16384 + } + }, + "openai/chat-completion/models/gpt-oss-20b": { + "id": "openai/chat-completion/models/gpt-oss-20b", + "family": "gpt-oss", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 131072, + "output": 16384 + } + }, + "baai/bge-reranker-v2-m3": { + "id": "BAAI/bge-reranker-v2-m3", + "family": "bge", + "reasoning": false, + "temperature": false, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 512, + "output": 512 + } + }, + "intfloat/multilingual-e5-large": { + "id": "intfloat/multilingual-e5-large", + "family": "text-embedding", + "reasoning": false, + "temperature": false, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 512, + "output": 1024 + } + }, + "mistralai/mistral-small-3.2-24b-instruct-2506": { + "id": "mistralai/Mistral-Small-3.2-24B-Instruct-2506", + "family": "mistral-small", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 32768, + "output": 8192 + } + }, + "lucidquery-nexus-coder": { + "id": "lucidquery-nexus-coder", + "family": "lucid", + "reasoning": true, + "temperature": false, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 250000, + "output": 60000 + } + }, + "lucidnova-rf1-100b": { + "id": "lucidnova-rf1-100b", + "family": "nova", + "reasoning": true, + "temperature": false, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 120000, + "output": 8000 + } + }, + "glm-4.6v-flash": { + "id": "glm-4.6v-flash", + "family": "glm", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image", + "video" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 32768 + } + }, + "deepseek-reasoner": { + "id": "deepseek-reasoner", + "family": "deepseek-thinking", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 64000, + "output": 65536, + "input": 64000 + } + }, + "deepseek-chat": { + "id": "deepseek-chat", + "family": "deepseek", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "pdf" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 8192, + "input": 128000 + } + }, + "qwen/qwen3-30b-a3b-2507": { + "id": "qwen/qwen3-30b-a3b-2507", + "family": "qwen", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 262144, + "output": 16384 + } + }, + "qwen/qwen3-coder-30b": { + "id": "qwen/qwen3-coder-30b", + "family": "qwen", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 262144, + "output": 65536 + } + }, + "prime-intellect/intellect-3": { + "id": "prime-intellect/intellect-3", + "family": "intellect", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 131072, + "output": 131072 + } + }, + "nvidia/nemotron-nano-9b-v2:free": { + "id": "nvidia/nemotron-nano-9b-v2:free", + "family": "nemotron", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 128000 + } + }, + "nvidia/nemotron-nano-12b-v2-vl:free": { + "id": "nvidia/nemotron-nano-12b-v2-vl:free", + "family": "nemotron", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 128000 + } + }, + "nvidia/nemotron-3-nano-30b-a3b:free": { + "id": "nvidia/nemotron-3-nano-30b-a3b:free", + "family": "nemotron", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 256000, + "output": 256000 + } + }, + "nvidia/nemotron-nano-9b-v2": { + "id": "nvidia/nemotron-nano-9b-v2", + "family": "nemotron", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 131072, + "output": 26215 + } + }, + "nvidia/nemotron-3-super-120b-a12b-free": { + "id": "nvidia/nemotron-3-super-120b-a12b-free", + "family": "nemotron", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 262144, + "output": 262144 + } + }, + "arcee-ai/trinity-large-preview:free": { + "id": "arcee-ai/trinity-large-preview:free", + "family": "trinity", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 131000, + "output": 26200 + } + }, + "arcee-ai/trinity-mini:free": { + "id": "arcee-ai/trinity-mini:free", + "family": "trinity-mini", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 131072, + "output": 131072 + } + }, + "liquid/lfm-2.5-1.2b-thinking:free": { + "id": "liquid/lfm-2.5-1.2b-thinking:free", + "family": "liquid", + "reasoning": true, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 131072, + "output": 32768 + } + }, + "liquid/lfm-2.5-1.2b-instruct:free": { + "id": "liquid/lfm-2.5-1.2b-instruct:free", + "family": "liquid", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 131072, + "output": 32768 + } + }, + "inception/mercury-2": { + "id": "inception/mercury-2", + "family": "mercury", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 50000 + } + }, + "inception/mercury": { + "id": "inception/mercury", + "family": "mercury", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 32000 + } + }, + "inception/mercury-coder": { + "id": "inception/mercury-coder", + "family": "mercury", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 32000 + } + }, + "sourceful/riverflow-v2-fast-preview": { + "id": "sourceful/riverflow-v2-fast-preview", + "family": "sourceful", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "image" + ] + }, + "limit": { + "context": 8192, + "output": 8192 + } + }, + "sourceful/riverflow-v2-max-preview": { + "id": "sourceful/riverflow-v2-max-preview", + "family": "sourceful", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "image" + ] + }, + "limit": { + "context": 8192, + "output": 8192 + } + }, + "sourceful/riverflow-v2-standard-preview": { + "id": "sourceful/riverflow-v2-standard-preview", + "family": "sourceful", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "image" + ] + }, + "limit": { + "context": 8192, + "output": 8192 + } + }, + "stepfun/step-3.5-flash:free": { + "id": "stepfun/step-3.5-flash:free", + "family": "step", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 256000, + "output": 256000 + } + }, + "cognitivecomputations/dolphin-mistral-24b-venice-edition:free": { + "id": "cognitivecomputations/dolphin-mistral-24b-venice-edition:free", + "family": "mistral", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 32768, + "output": 32768 + } + }, + "deepseek/deepseek-v3.1-terminus:exacto": { + "id": "deepseek/deepseek-v3.1-terminus:exacto", + "family": "deepseek", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 131072, + "output": 65536 + } + }, + "deepseek/deepseek-v3.2-speciale": { + "id": "deepseek/deepseek-v3.2-speciale", + "family": "deepseek", + "reasoning": true, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text", + "pdf" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 163000, + "output": 65536, + "input": 163000 + } + }, + "deepseek/deepseek-chat-v3.1": { + "id": "deepseek/deepseek-chat-v3.1", + "family": "deepseek", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 32768, + "output": 7168 + } + }, + "deepseek/deepseek-chat-v3-0324": { + "id": "deepseek/deepseek-chat-v3-0324", + "family": "deepseek", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 163840, + "output": 65536 + } + }, + "openrouter/free": { + "id": "openrouter/free", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "image", + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 200000, + "input": 200000, + "output": 32768 + } + }, + "moonshotai/kimi-k2-0905:exacto": { + "id": "moonshotai/kimi-k2-0905:exacto", + "family": "kimi", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 262144, + "output": 16384 + } + }, + "moonshotai/kimi-k2:free": { + "id": "moonshotai/kimi-k2:free", + "family": "kimi", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 32800, + "output": 32800 + } + }, + "google/gemini-2.5-flash-lite-preview-09-2025": { + "id": "google/gemini-2.5-flash-lite-preview-09-2025", + "family": "gemini-flash-lite", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "audio", + "image", + "pdf", + "text", + "video" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 1048576, + "output": 65536 + } + }, + "google/gemini-3.1-pro-preview-customtools": { + "id": "google/gemini-3.1-pro-preview-customtools", + "family": "gemini-pro", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "audio", + "image", + "pdf", + "text", + "video" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 1048576, + "output": 65536 + } + }, + "google/gemini-2.5-pro-preview-06-05": { + "id": "google/gemini-2.5-pro-preview-06-05", + "family": "gemini-pro", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image", + "audio", + "video", + "pdf" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 1048576, + "output": 65536 + } + }, + "google/gemma-3n-e4b-it:free": { + "id": "google/gemma-3n-e4b-it:free", + "family": "gemma", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 8192, + "output": 2000 + } + }, + "google/gemini-2.5-flash-preview-09-2025": { + "id": "google/gemini-2.5-flash-preview-09-2025", + "family": "gemini-flash", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image", + "audio", + "video", + "pdf" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 1048576, + "output": 65536 + } + }, + "google/gemini-2.5-pro-preview-05-06": { + "id": "google/gemini-2.5-pro-preview-05-06", + "family": "gemini-pro", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "audio", + "image", + "pdf", + "text", + "video" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 1048576, + "output": 65535 + } + }, + "google/gemma-3n-e2b-it:free": { + "id": "google/gemma-3n-e2b-it:free", + "family": "gemma", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 8192, + "output": 2000 + } + }, + "google/gemini-2.0-flash-001": { + "id": "google/gemini-2.0-flash-001", + "family": "gemini-flash", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "audio", + "image", + "pdf", + "text", + "video" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 1048576, + "output": 8192 + } + }, + "google/gemma-3-12b-it:free": { + "id": "google/gemma-3-12b-it:free", + "family": "gemma", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 32768, + "output": 8192 + } + }, + "google/gemma-2-9b-it": { + "id": "google/gemma-2-9b-it", + "family": "gemma", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 8192, + "output": 1639 + } + }, + "google/gemma-3-4b-it:free": { + "id": "google/gemma-3-4b-it:free", + "family": "gemma", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 32768, + "output": 8192 + } + }, + "google/gemma-3-4b-it": { + "id": "google/gemma-3-4b-it", + "family": "gemma", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "image", + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 131072, + "output": 19200 + } + }, + "google/gemma-3-27b-it:free": { + "id": "google/gemma-3-27b-it:free", + "family": "gemma", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 131072, + "output": 8192 + } + }, + "z-ai/glm-4.6:exacto": { + "id": "z-ai/glm-4.6:exacto", + "family": "glm", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 200000, + "output": 128000 + } + }, + "z-ai/glm-4.7-flash": { + "id": "z-ai/glm-4.7-flash", + "family": "glm", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 202752, + "output": 40551 + } + }, + "z-ai/glm-4.5-air:free": { + "id": "z-ai/glm-4.5-air:free", + "family": "glm-air", + "reasoning": true, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 96000 + } + }, + "z-ai/glm-4.5v": { + "id": "z-ai/glm-4.5v", + "family": "glmv", + "reasoning": true, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 64000, + "output": 96000, + "input": 64000 + } + }, + "qwen/qwen3-coder:free": { + "id": "qwen/qwen3-coder:free", + "family": "qwen", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 262144, + "output": 66536 + } + }, + "qwen/qwen3-coder-flash": { + "id": "qwen/qwen3-coder-flash", + "family": "qwen", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 1000000, + "output": 65536 + } + }, + "qwen/qwen3-coder:exacto": { + "id": "qwen/qwen3-coder:exacto", + "family": "qwen", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 131072, + "output": 32768 + } + }, + "qwen/qwen-2.5-coder-32b-instruct": { + "id": "qwen/qwen-2.5-coder-32b-instruct", + "family": "qwen", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 32768, + "output": 8192 + } + }, + "qwen/qwen3.5-plus-02-15": { + "id": "qwen/qwen3.5-plus-02-15", + "family": "qwen", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "image", + "text", + "video" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 1000000, + "output": 65536 + } + }, + "qwen/qwen3-235b-a22b-07-25": { + "id": "qwen/qwen3-235b-a22b-07-25", + "family": "qwen", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 262144, + "output": 131072 + } + }, + "qwen/qwen3-next-80b-a3b-instruct:free": { + "id": "qwen/qwen3-next-80b-a3b-instruct:free", + "family": "qwen", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 262144, + "output": 262144 + } + }, + "qwen/qwen3-4b:free": { + "id": "qwen/qwen3-4b:free", + "family": "qwen", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 40960, + "output": 40960 + } + }, + "x-ai/grok-3": { + "id": "x-ai/grok-3", + "family": "grok", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 131072, + "output": 26215 + } + }, + "x-ai/grok-3-mini-beta": { + "id": "x-ai/grok-3-mini-beta", + "family": "grok", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 131072, + "output": 26215 + } + }, + "x-ai/grok-3-mini": { + "id": "x-ai/grok-3-mini", + "family": "grok", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 131072, + "output": 26215 + } + }, + "x-ai/grok-4.20-multi-agent-beta": { + "id": "x-ai/grok-4.20-multi-agent-beta", + "family": "grok", + "reasoning": true, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "image", + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 2000000, + "output": 32768 + } + }, + "x-ai/grok-4.20-beta": { + "id": "x-ai/grok-4.20-beta", + "family": "grok", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "image", + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 2000000, + "output": 32768 + } + }, + "x-ai/grok-3-beta": { + "id": "x-ai/grok-3-beta", + "family": "grok", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 131072, + "output": 26215 + } + }, + "meta-llama/llama-3.3-70b-instruct:free": { + "id": "meta-llama/llama-3.3-70b-instruct:free", + "family": "llama", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 131072, + "output": 131072 + } + }, + "meta-llama/llama-3.2-11b-vision-instruct": { + "id": "meta-llama/llama-3.2-11b-vision-instruct", + "family": "llama", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 131072, + "output": 16384 + } + }, + "meta-llama/llama-3.2-3b-instruct:free": { + "id": "meta-llama/llama-3.2-3b-instruct:free", + "family": "llama", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 131072, + "output": 131072 + } + }, + "mistralai/devstral-medium-2507": { + "id": "mistralai/devstral-medium-2507", + "family": "devstral", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 131072, + "output": 131072 + } + }, + "mistralai/mistral-medium-3": { + "id": "mistralai/mistral-medium-3", + "family": "mistral-medium", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 131072, + "output": 32768, + "input": 131072 + } + }, + "mistralai/codestral-2508": { + "id": "mistralai/codestral-2508", + "family": "codestral", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 256000, + "output": 32768, + "input": 256000 + } + }, + "mistralai/mistral-small-3.1-24b-instruct": { + "id": "mistralai/mistral-small-3.1-24b-instruct", + "family": "mistral-small", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "image", + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 131072 + } + }, + "mistralai/devstral-2512": { + "id": "mistralai/devstral-2512", + "family": "devstral", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 262144, + "output": 65536 + } + }, + "mistralai/mistral-small-3.2-24b-instruct": { + "id": "mistralai/mistral-small-3.2-24b-instruct", + "family": "mistral-small", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "image", + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 131072, + "output": 131072 + } + }, + "mistralai/devstral-small-2507": { + "id": "mistralai/devstral-small-2507", + "family": "devstral", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 131072, + "output": 131072 + } + }, + "mistralai/mistral-medium-3.1": { + "id": "mistralai/mistral-medium-3.1", + "family": "mistral-medium", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 131072, + "output": 32768, + "input": 131072 + } + }, + "openai/gpt-oss-120b:exacto": { + "id": "openai/gpt-oss-120b:exacto", + "family": "gpt-oss", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 131072, + "output": 32768 + } + }, + "openai/gpt-5.2-chat": { + "id": "openai/gpt-5.2-chat", + "family": "gpt", + "reasoning": true, + "temperature": false, + "toolCall": false, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 400000, + "output": 16384, + "input": 400000 + } + }, + "openai/gpt-5-image": { + "id": "openai/gpt-5-image", + "family": "gpt", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "image", + "pdf", + "text" + ], + "output": [ + "image", + "text" + ] + }, + "limit": { + "context": 400000, + "output": 128000 + } + }, + "openai/gpt-oss-20b:free": { + "id": "openai/gpt-oss-20b:free", + "family": "gpt-oss", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 131072, + "output": 32768 + } + }, + "openai/gpt-oss-safeguard-20b": { + "id": "openai/gpt-oss-safeguard-20b", + "family": "gpt-oss", + "reasoning": true, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 16384, + "input": 128000 + } + }, + "openai/gpt-oss-120b:free": { + "id": "openai/gpt-oss-120b:free", + "family": "gpt-oss", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 131072, + "output": 32768 + } + }, + "minimax/minimax-m1": { + "id": "minimax/minimax-m1", + "family": "minimax", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 1000000, + "output": 40000 + } + }, + "minimax/minimax-01": { + "id": "minimax/minimax-01", + "family": "minimax", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text", + "pdf" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 1000192, + "output": 16384, + "input": 1000192 + } + }, + "bytedance-seed/seedream-4.5": { + "id": "bytedance-seed/seedream-4.5", + "family": "seed", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "image", + "text" + ], + "output": [ + "image" + ] + }, + "limit": { + "context": 4096, + "output": 4096 + } + }, + "black-forest-labs/flux.2-pro": { + "id": "black-forest-labs/flux.2-pro", + "family": "flux", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "image", + "text" + ], + "output": [ + "image" + ] + }, + "limit": { + "context": 46864, + "output": 46864 + } + }, + "black-forest-labs/flux.2-flex": { + "id": "black-forest-labs/flux.2-flex", + "family": "flux", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "image", + "text" + ], + "output": [ + "image" + ] + }, + "limit": { + "context": 67344, + "output": 67344 + } + }, + "black-forest-labs/flux.2-max": { + "id": "black-forest-labs/flux.2-max", + "family": "flux", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "image", + "text" + ], + "output": [ + "image" + ] + }, + "limit": { + "context": 46864, + "output": 46864 + } + }, + "black-forest-labs/flux.2-klein-4b": { + "id": "black-forest-labs/flux.2-klein-4b", + "family": "flux", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "image", + "text" + ], + "output": [ + "image" + ] + }, + "limit": { + "context": 40960, + "output": 40960 + } + }, + "nousresearch/hermes-3-llama-3.1-405b:free": { + "id": "nousresearch/hermes-3-llama-3.1-405b:free", + "family": "hermes", + "reasoning": true, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 131072, + "output": 131072 + } + }, + "ai21-labs/ai21-jamba-1.5-mini": { + "id": "ai21-labs/ai21-jamba-1.5-mini", + "family": "jamba", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 256000, + "output": 4096 + } + }, + "ai21-labs/ai21-jamba-1.5-large": { + "id": "ai21-labs/ai21-jamba-1.5-large", + "family": "jamba", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 256000, + "output": 4096 + } + }, + "microsoft/mai-ds-r1": { + "id": "microsoft/mai-ds-r1", + "family": "mai", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 65536, + "output": 8192 + } + }, + "microsoft/phi-3.5-mini-instruct": { + "id": "microsoft/phi-3.5-mini-instruct", + "family": "phi", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 4096 + } + }, + "microsoft/phi-4": { + "id": "microsoft/phi-4", + "family": "phi", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 16384, + "output": 16384 + } + }, + "microsoft/phi-3-mini-4k-instruct": { + "id": "microsoft/phi-3-mini-4k-instruct", + "family": "phi", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 4096, + "output": 1024 + } + }, + "microsoft/phi-4-mini-reasoning": { + "id": "microsoft/phi-4-mini-reasoning", + "family": "phi", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 4096 + } + }, + "microsoft/phi-3-mini-128k-instruct": { + "id": "microsoft/phi-3-mini-128k-instruct", + "family": "phi", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 4096 + } + }, + "microsoft/phi-4-reasoning": { + "id": "microsoft/phi-4-reasoning", + "family": "phi", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 4096 + } + }, + "core42/jais-30b-chat": { + "id": "core42/jais-30b-chat", + "family": "jais", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 8192, + "output": 2048 + } + }, + "mistral-ai/ministral-3b": { + "id": "mistral-ai/ministral-3b", + "family": "ministral", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 8192 + } + }, + "mistral-ai/mistral-medium-2505": { + "id": "mistral-ai/mistral-medium-2505", + "family": "mistral-medium", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 32768 + } + }, + "mistral-ai/mistral-nemo": { + "id": "mistral-ai/mistral-nemo", + "family": "mistral-nemo", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 8192 + } + }, + "mistral-ai/mistral-large-2411": { + "id": "mistral-ai/mistral-large-2411", + "family": "mistral-large", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 32768 + } + }, + "mistral-ai/mistral-small-2503": { + "id": "mistral-ai/mistral-small-2503", + "family": "mistral-small", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 32768 + } + }, + "mistral-ai/codestral-2501": { + "id": "mistral-ai/codestral-2501", + "family": "codestral", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 32000, + "output": 8192 + } + }, + "deepseek/deepseek-r1": { + "id": "deepseek/deepseek-r1", + "family": "deepseek-thinking", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 64000, + "output": 16000 + } + }, + "meta/llama-3.2-90b-vision-instruct": { + "id": "meta/llama-3.2-90b-vision-instruct", + "family": "llama", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image", + "audio" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 8192 + } + }, + "meta/meta-llama-3.1-405b-instruct": { + "id": "meta/meta-llama-3.1-405b-instruct", + "family": "llama", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 32768 + } + }, + "meta/meta-llama-3-8b-instruct": { + "id": "meta/meta-llama-3-8b-instruct", + "family": "llama", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 8192, + "output": 2048 + } + }, + "meta/meta-llama-3-70b-instruct": { + "id": "meta/meta-llama-3-70b-instruct", + "family": "llama", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 8192, + "output": 2048 + } + }, + "meta/meta-llama-3.1-70b-instruct": { + "id": "meta/meta-llama-3.1-70b-instruct", + "family": "llama", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 32768 + } + }, + "meta/meta-llama-3.1-8b-instruct": { + "id": "meta/meta-llama-3.1-8b-instruct", + "family": "llama", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 32768 + } + }, + "meta/llama-4-maverick-17b-128e-instruct-fp8": { + "id": "meta/llama-4-maverick-17b-128e-instruct-fp8", + "family": "llama", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 8192 + } + }, + "openai/o1-preview": { + "id": "openai/o1-preview", + "family": "o", + "reasoning": true, + "temperature": false, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 32768, + "input": 128000 + } + }, + "openai/o1-mini": { + "id": "openai/o1-mini", + "family": "o-mini", + "reasoning": true, + "temperature": false, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 65536 + } + }, + "cohere/cohere-command-a": { + "id": "cohere/cohere-command-a", + "family": "command-a", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 4096 + } + }, + "cohere/cohere-command-r-plus-08-2024": { + "id": "cohere/cohere-command-r-plus-08-2024", + "family": "command-r", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 4096 + } + }, + "cohere/cohere-command-r": { + "id": "cohere/cohere-command-r", + "family": "command-r", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 4096 + } + }, + "cohere/cohere-command-r-08-2024": { + "id": "cohere/cohere-command-r-08-2024", + "family": "command-r", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 4096 + } + }, + "cohere/cohere-command-r-plus": { + "id": "cohere/cohere-command-r-plus", + "family": "command-r", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 4096 + } + }, + "qwen-max-latest": { + "id": "qwen-max-latest", + "family": "qwen", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 131072, + "output": 8192 + } + }, + "qwen3-max-2025-09-23": { + "id": "qwen3-max-2025-09-23", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 258048, + "output": 65536 + } + }, + "gemini-2.5-flash-lite-preview-09-2025": { + "id": "gemini-2.5-flash-lite-preview-09-2025", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image", + "pdf" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 1048756, + "output": 65536, + "input": 1048756 + }, + "family": "gemini-flash-lite" + }, + "claude-opus-4-1-20250805-thinking": { + "id": "claude-opus-4-1-20250805-thinking", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 200000, + "output": 32000 + } + }, + "gemini-2.5-flash-preview-09-2025": { + "id": "gemini-2.5-flash-preview-09-2025", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image", + "pdf" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 1048756, + "output": 65536, + "input": 1048756 + }, + "family": "gemini-flash" + }, + "grok-4-1-fast-reasoning": { + "id": "grok-4-1-fast-reasoning", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 8192, + "input": 128000 + }, + "family": "grok" + }, + "kimi-k2-0905-preview": { + "id": "kimi-k2-0905-preview", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 262144, + "output": 262144 + }, + "family": "kimi" + }, + "claude-sonnet-4-5-20250929-thinking": { + "id": "claude-sonnet-4-5-20250929-thinking", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image", + "pdf" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 1000000, + "output": 64000, + "input": 1000000 + } + }, + "doubao-seed-1-6-vision-250815": { + "id": "doubao-seed-1-6-vision-250815", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 256000, + "output": 32000 + } + }, + "doubao-seed-1-6-thinking-250715": { + "id": "doubao-seed-1-6-thinking-250715", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 256000, + "output": 16000 + } + }, + "doubao-seed-1-8-251215": { + "id": "doubao-seed-1-8-251215", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 8192, + "input": 128000 + } + }, + "ministral-14b-2512": { + "id": "ministral-14b-2512", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 128000 + } + }, + "gemini-2.5-flash-nothink": { + "id": "gemini-2.5-flash-nothink", + "family": "gemini-flash", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 1000000, + "output": 65536 + } + }, + "claude-opus-4-5-20251101-thinking": { + "id": "claude-opus-4-5-20251101-thinking", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 200000, + "output": 64000 + } + }, + "gemini-3-pro-image-preview": { + "id": "gemini-3-pro-image-preview", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 1048756, + "output": 65536, + "input": 1048756 + } + }, + "gpt-5-thinking": { + "id": "gpt-5-thinking", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 400000, + "output": 128000 + } + }, + "deepseek-v3.2-thinking": { + "id": "deepseek-v3.2-thinking", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 128000 + } + }, + "chatgpt-4o-latest": { + "id": "chatgpt-4o-latest", + "family": "gpt", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 16384 + } + }, + "kimi-k2-thinking-turbo": { + "id": "kimi-k2-thinking-turbo", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 262144, + "output": 262144 + }, + "family": "kimi-thinking" + }, + "doubao-seed-code-preview-251028": { + "id": "doubao-seed-code-preview-251028", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 256000, + "output": 32000 + } + }, + "grok-4.1": { + "id": "grok-4.1", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 200000, + "output": 64000 + } + }, + "claude-sonnet-4.6": { + "id": "claude-sonnet-4.6", + "family": "claude-sonnet", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 200000, + "input": 128000, + "output": 32000 + } + }, + "claude-haiku-4.5": { + "id": "claude-haiku-4.5", + "family": "claude-haiku", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 144000, + "input": 128000, + "output": 32000 + } + }, + "claude-opus-4.5": { + "id": "claude-opus-4.5", + "family": "claude-opus", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 160000, + "input": 128000, + "output": 32000 + } + }, + "claude-sonnet-4.5": { + "id": "claude-sonnet-4.5", + "family": "claude-sonnet", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 144000, + "input": 128000, + "output": 32000 + } + }, + "claude-opus-4.6": { + "id": "claude-opus-4.6", + "family": "claude-opus", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 144000, + "input": 128000, + "output": 64000 + } + }, + "claude-opus-41": { + "id": "claude-opus-41", + "family": "claude-opus", + "reasoning": true, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 80000, + "output": 16000 + } + }, + "kimi-k2-0711-preview": { + "id": "kimi-k2-0711-preview", + "family": "kimi", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 131072, + "output": 16384 + } + }, + "gemini-embedding-001": { + "id": "gemini-embedding-001", + "family": "gemini", + "reasoning": false, + "temperature": false, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 2048, + "output": 3072 + } + }, + "gemini-3.1-pro-preview-customtools": { + "id": "gemini-3.1-pro-preview-customtools", + "family": "gemini-pro", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image", + "video", + "audio", + "pdf" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 1048576, + "output": 65536 + } + }, + "gemini-2.5-pro-preview-06-05": { + "id": "gemini-2.5-pro-preview-06-05", + "family": "gemini-pro", + "reasoning": true, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 1048756, + "output": 65536, + "input": 1048756 + } + }, + "gemini-2.5-flash-preview-04-17": { + "id": "gemini-2.5-flash-preview-04-17", + "family": "gemini-flash", + "reasoning": true, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 1048756, + "output": 65536, + "input": 1048756 + } + }, + "gemini-2.5-pro-preview-05-06": { + "id": "gemini-2.5-pro-preview-05-06", + "family": "gemini-pro", + "reasoning": true, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 1048756, + "output": 65536, + "input": 1048756 + } + }, + "gemini-2.5-flash-preview-05-20": { + "id": "gemini-2.5-flash-preview-05-20", + "family": "gemini-flash", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 1048000, + "output": 65536, + "input": 1048000 + } + }, + "gemini-flash-latest": { + "id": "gemini-flash-latest", + "family": "gemini-flash", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image", + "audio", + "video", + "pdf" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 1048576, + "output": 65536 + } + }, + "gemini-2.5-flash-lite-preview-06-17": { + "id": "gemini-2.5-flash-lite-preview-06-17", + "family": "gemini-flash-lite", + "reasoning": true, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 1048756, + "output": 65536, + "input": 1048756 + } + }, + "gemini-flash-lite-latest": { + "id": "gemini-flash-lite-latest", + "family": "gemini-flash-lite", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image", + "audio", + "video", + "pdf" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 1048576, + "output": 65536 + } + }, + "zai-org/glm-5-maas": { + "id": "zai-org/glm-5-maas", + "family": "glm", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 202752, + "output": 131072 + } + }, + "zai-org/glm-4.7-maas": { + "id": "zai-org/glm-4.7-maas", + "family": "glm", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "pdf" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 200000, + "output": 128000 + } + }, + "deepseek-ai/deepseek-v3.1-maas": { + "id": "deepseek-ai/deepseek-v3.1-maas", + "family": "deepseek", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "pdf" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 163840, + "output": 32768 + } + }, + "qwen/qwen3-235b-a22b-instruct-2507-maas": { + "id": "qwen/qwen3-235b-a22b-instruct-2507-maas", + "family": "qwen", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 262144, + "output": 16384 + } + }, + "meta/llama-4-maverick-17b-128e-instruct-maas": { + "id": "meta/llama-4-maverick-17b-128e-instruct-maas", + "family": "llama", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 524288, + "output": 8192 + } + }, + "meta/llama-3.3-70b-instruct-maas": { + "id": "meta/llama-3.3-70b-instruct-maas", + "family": "llama", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 8192 + } + }, + "openai/gpt-oss-20b-maas": { + "id": "openai/gpt-oss-20b-maas", + "family": "gpt-oss", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 131072, + "output": 32768 + } + }, + "openai/gpt-oss-120b-maas": { + "id": "openai/gpt-oss-120b-maas", + "family": "gpt-oss", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 131072, + "output": 32768 + } + }, + "gemma-3-27b": { + "id": "gemma-3-27b", + "family": "gemma", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 8192 + } + }, + "qwen3-embedding-4b": { + "id": "qwen3-embedding-4b", + "family": "qwen", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 32000, + "output": 2560 + } + }, + "qwen3-coder-30b-a3b": { + "id": "qwen3-coder-30b-a3b", + "family": "qwen", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 32768 + } + }, + "gemini-3.1-flash-image-preview": { + "id": "gemini-3.1-flash-image-preview", + "family": "gemini-flash", + "reasoning": true, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text", + "image", + "pdf" + ], + "output": [ + "text", + "image" + ] + }, + "limit": { + "context": 131072, + "output": 32768 + } + }, + "gemini-live-2.5-flash": { + "id": "gemini-live-2.5-flash", + "family": "gemini-flash", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image", + "audio", + "video" + ], + "output": [ + "text", + "audio" + ] + }, + "limit": { + "context": 128000, + "output": 8000 + } + }, + "gemini-live-2.5-flash-preview-native-audio": { + "id": "gemini-live-2.5-flash-preview-native-audio", + "family": "gemini-flash", + "reasoning": true, + "temperature": false, + "toolCall": true, + "modalities": { + "input": [ + "text", + "audio", + "video" + ], + "output": [ + "text", + "audio" + ] + }, + "limit": { + "context": 131072, + "output": 65536 + } + }, + "gemini-2.5-flash-preview-tts": { + "id": "gemini-2.5-flash-preview-tts", + "family": "gemini-flash", + "reasoning": false, + "temperature": false, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "audio" + ] + }, + "limit": { + "context": 8000, + "output": 16000 + } + }, + "gemini-2.5-pro-preview-tts": { + "id": "gemini-2.5-pro-preview-tts", + "family": "gemini-flash", + "reasoning": false, + "temperature": false, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "audio" + ] + }, + "limit": { + "context": 8000, + "output": 16000 + } + }, + "gemini-2.5-flash-image-preview": { + "id": "gemini-2.5-flash-image-preview", + "family": "gemini-flash", + "reasoning": true, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text", + "image" + ] + }, + "limit": { + "context": 32768, + "output": 32768 + } + }, + "gemini-1.5-flash-8b": { + "id": "gemini-1.5-flash-8b", + "family": "gemini-flash", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image", + "audio", + "video" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 1000000, + "output": 8192 + } + }, + "gemini-1.5-flash": { + "id": "gemini-1.5-flash", + "family": "gemini-flash", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image", + "audio", + "video" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 1000000, + "output": 8192 + } + }, + "gemini-1.5-pro": { + "id": "gemini-1.5-pro", + "family": "gemini-pro", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image", + "audio", + "video" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 1000000, + "output": 8192 + } + }, + "anthropic--claude-4.5-opus": { + "id": "anthropic--claude-4.5-opus", + "family": "claude-opus", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image", + "pdf" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 200000, + "output": 64000 + } + }, + "anthropic--claude-4-sonnet": { + "id": "anthropic--claude-4-sonnet", + "family": "claude-sonnet", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image", + "pdf" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 200000, + "output": 64000 + } + }, + "anthropic--claude-4.5-sonnet": { + "id": "anthropic--claude-4.5-sonnet", + "family": "claude-sonnet", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image", + "pdf" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 200000, + "output": 64000 + } + }, + "anthropic--claude-3-sonnet": { + "id": "anthropic--claude-3-sonnet", + "family": "claude-sonnet", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image", + "pdf" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 200000, + "output": 4096 + } + }, + "anthropic--claude-3.7-sonnet": { + "id": "anthropic--claude-3.7-sonnet", + "family": "claude-sonnet", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image", + "pdf" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 200000, + "output": 64000 + } + }, + "sonar": { + "id": "sonar", + "family": "sonar", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 127000, + "output": 128000, + "input": 127000 + } + }, + "anthropic--claude-3.5-sonnet": { + "id": "anthropic--claude-3.5-sonnet", + "family": "claude-sonnet", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image", + "pdf" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 200000, + "output": 8192 + } + }, + "sonar-deep-research": { + "id": "sonar-deep-research", + "family": "sonar-deep-research", + "reasoning": false, + "temperature": false, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 60000, + "output": 128000, + "input": 60000 + } + }, + "anthropic--claude-4.6-sonnet": { + "id": "anthropic--claude-4.6-sonnet", + "family": "claude-sonnet", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image", + "pdf" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 1000000, + "output": 64000 + } + }, + "anthropic--claude-4.5-haiku": { + "id": "anthropic--claude-4.5-haiku", + "family": "claude-haiku", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image", + "pdf" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 200000, + "output": 64000 + } + }, + "anthropic--claude-3-opus": { + "id": "anthropic--claude-3-opus", + "family": "claude-opus", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image", + "pdf" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 200000, + "output": 4096 + } + }, + "sonar-pro": { + "id": "sonar-pro", + "family": "sonar-pro", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 200000, + "output": 128000, + "input": 200000 + } + }, + "anthropic--claude-3-haiku": { + "id": "anthropic--claude-3-haiku", + "family": "claude-haiku", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image", + "pdf" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 200000, + "output": 4096 + } + }, + "anthropic--claude-4.6-opus": { + "id": "anthropic--claude-4.6-opus", + "family": "claude-opus", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image", + "pdf" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 1000000, + "output": 128000 + } + }, + "anthropic--claude-4-opus": { + "id": "anthropic--claude-4-opus", + "family": "claude-opus", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image", + "pdf" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 200000, + "output": 32000 + } + }, + "google-gemma-3-27b-it": { + "id": "google-gemma-3-27b-it", + "family": "gemma", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 198000, + "output": 16384 + } + }, + "openai-gpt-4o-2024-11-20": { + "id": "openai-gpt-4o-2024-11-20", + "family": "gpt", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 16384 + } + }, + "claude-opus-45": { + "id": "claude-opus-45", + "family": "claude-opus", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image", + "pdf" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 198000, + "output": 49500 + } + }, + "zai-org-glm-5": { + "id": "zai-org-glm-5", + "family": "glm", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 198000, + "output": 32000 + } + }, + "zai-org-glm-4.7": { + "id": "zai-org-glm-4.7", + "family": "glm", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 198000, + "output": 16384 + } + }, + "zai-org-glm-4.6": { + "id": "zai-org-glm-4.6", + "family": "glm", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 198000, + "output": 16384 + } + }, + "openai-gpt-53-codex": { + "id": "openai-gpt-53-codex", + "family": "gpt-codex", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 400000, + "output": 128000 + } + }, + "kimi-k2-5": { + "id": "kimi-k2-5", + "family": "kimi", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 256000, + "output": 65536 + } + }, + "mistral-small-3-2-24b-instruct": { + "id": "mistral-small-3-2-24b-instruct", + "family": "mistral-small", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 256000, + "output": 16384 + } + }, + "mistral-31-24b": { + "id": "mistral-31-24b", + "family": "mistral", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 4096 + } + }, + "grok-4-20-multi-agent-beta": { + "id": "grok-4-20-multi-agent-beta", + "family": "grok-beta", + "reasoning": true, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 2000000, + "output": 128000 + } + }, + "openai-gpt-54-pro": { + "id": "openai-gpt-54-pro", + "family": "gpt-pro", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 1000000, + "output": 128000 + } + }, + "qwen3-4b": { + "id": "qwen3-4b", + "family": "qwen", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 32000, + "output": 4096 + } + }, + "grok-4-20-beta": { + "id": "grok-4-20-beta", + "family": "grok-beta", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 2000000, + "output": 128000 + } + }, + "olafangensan-glm-4.7-flash-heretic": { + "id": "olafangensan-glm-4.7-flash-heretic", + "family": "glm-flash", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 200000, + "output": 24000 + } + }, + "minimax-m25": { + "id": "minimax-m25", + "family": "minimax", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 198000, + "output": 32768 + } + }, + "zai-org-glm-4.7-flash": { + "id": "zai-org-glm-4.7-flash", + "family": "glm-flash", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 16384 + } + }, + "qwen3-coder-480b-a35b-instruct-turbo": { + "id": "qwen3-coder-480b-a35b-instruct-turbo", + "family": "qwen", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 256000, + "output": 65536 + } + }, + "openai-gpt-oss-120b": { + "id": "openai-gpt-oss-120b", + "family": "gpt-oss", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 16384 + } + }, + "grok-41-fast": { + "id": "grok-41-fast", + "family": "grok", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 1000000, + "output": 30000 + } + }, + "openai-gpt-52": { + "id": "openai-gpt-52", + "family": "gpt", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 256000, + "output": 65536 + } + }, + "openai-gpt-54": { + "id": "openai-gpt-54", + "family": "gpt", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 1000000, + "output": 131072 + } + }, + "gemini-3-1-pro-preview": { + "id": "gemini-3-1-pro-preview", + "family": "gemini-pro", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image", + "video", + "audio", + "pdf" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 1000000, + "output": 64000 + } + }, + "openai-gpt-4o-mini-2024-07-18": { + "id": "openai-gpt-4o-mini-2024-07-18", + "family": "gpt-mini", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 16384 + } + }, + "llama-3.3-70b": { + "id": "llama-3.3-70b", + "family": "llama", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 4096 + } + }, + "qwen3-next-80b": { + "id": "qwen3-next-80b", + "family": "qwen", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 256000, + "output": 16384 + } + }, + "hermes-3-llama-3.1-405b": { + "id": "hermes-3-llama-3.1-405b", + "family": "hermes", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 16384 + } + }, + "qwen3-5-9b": { + "id": "qwen3-5-9b", + "family": "qwen", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 256000, + "output": 65536 + } + }, + "minimax-m21": { + "id": "minimax-m21", + "family": "minimax", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 198000, + "output": 32768 + } + }, + "qwen3-5-35b-a3b": { + "id": "qwen3-5-35b-a3b", + "family": "qwen", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image", + "video" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 256000, + "output": 65536 + } + }, + "llama-3.2-3b": { + "id": "llama-3.2-3b", + "family": "llama", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 4096 + } + }, + "venice-uncensored": { + "id": "venice-uncensored", + "family": "venice", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 16384, + "input": 128000 + } + }, + "nvidia-nemotron-3-nano-30b-a3b": { + "id": "nvidia-nemotron-3-nano-30b-a3b", + "family": "nemotron", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 16384 + } + }, + "openai-gpt-52-codex": { + "id": "openai-gpt-52-codex", + "family": "gpt-codex", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 256000, + "output": 65536 + } + }, + "minimax-m27": { + "id": "minimax-m27", + "family": "minimax", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 198000, + "output": 32768 + } + }, + "venice-uncensored-role-play": { + "id": "venice-uncensored-role-play", + "family": "venice", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 4096 + } + }, + "claude-sonnet-45": { + "id": "claude-sonnet-45", + "family": "claude-sonnet", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 198000, + "output": 49500 + } + }, + "nova-2-lite-v1": { + "id": "nova-2-lite-v1", + "family": "nova-lite", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image", + "video", + "pdf" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 1000000, + "output": 64000 + } + }, + "nova-2-pro-v1": { + "id": "nova-2-pro-v1", + "family": "nova-pro", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image", + "video", + "pdf" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 1000000, + "output": 64000 + } + }, + "glm-4.7-flashx": { + "id": "glm-4.7-flashx", + "family": "glm-flash", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 200000, + "output": 131072 + } + }, + "public/deepseek-v3": { + "id": "public/deepseek-v3", + "family": "deepseek", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 131072, + "output": 8192 + } + }, + "public/deepseek-r1": { + "id": "public/deepseek-r1", + "family": "deepseek-thinking", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 131072, + "output": 32000 + } + }, + "public/minimax-m25": { + "id": "public/minimax-m25", + "family": "minimax", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 204800, + "output": 131072 + } + }, + "gpt-5-4": { + "id": "gpt-5-4", + "family": "gpt", + "reasoning": true, + "temperature": false, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 272000, + "output": 128000 + } + }, + "deepseek-v3-2": { + "id": "deepseek-v3-2", + "family": "deepseek", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 8192 + } + }, + "minimax-m2-5": { + "id": "minimax-m2-5", + "family": "minimax", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 192000, + "output": 8192 + } + }, + "gpt-5-3-codex": { + "id": "gpt-5-3-codex", + "family": "gpt", + "reasoning": true, + "temperature": false, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 400000, + "output": 128000 + } + }, + "meta-llama-3_3-70b-instruct": { + "id": "meta-llama-3_3-70b-instruct", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 131072, + "output": 131072 + } + }, + "mistral-7b-instruct-v0.3": { + "id": "mistral-7b-instruct-v0.3", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 65536, + "output": 65536 + } + }, + "qwen2.5-coder-32b-instruct": { + "id": "qwen2.5-coder-32b-instruct", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 32768, + "output": 32768 + } + }, + "mixtral-8x7b-instruct-v0.1": { + "id": "mixtral-8x7b-instruct-v0.1", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 32768, + "output": 32768 + } + }, + "neuralmagic/meta-llama-3.1-8b-instruct-fp8": { + "id": "neuralmagic/Meta-Llama-3.1-8B-Instruct-FP8", + "family": "llama", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 8192 + } + }, + "neuralmagic/mistral-nemo-instruct-2407-fp8": { + "id": "neuralmagic/Mistral-Nemo-Instruct-2407-FP8", + "family": "mistral", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 8192 + } + }, + "qwen/qwen3-vl-embedding-8b": { + "id": "Qwen/Qwen3-VL-Embedding-8B", + "family": "qwen", + "reasoning": false, + "temperature": false, + "toolCall": false, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 32000, + "output": 4096 + } + }, + "qwen/qwen3-vl-235b-a22b-instruct-fp8": { + "id": "Qwen/Qwen3-VL-235B-A22B-Instruct-FP8", + "family": "qwen", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 218000, + "output": 8192 + } + }, + "cortecs/llama-3.3-70b-instruct-fp8-dynamic": { + "id": "cortecs/Llama-3.3-70B-Instruct-FP8-Dynamic", + "family": "llama", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 8192 + } + }, + "speakleash/bielik-11b-v2.6-instruct": { + "id": "speakleash/Bielik-11B-v2.6-Instruct", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 32000, + "output": 32000 + } + }, + "speakleash/bielik-11b-v3.0-instruct": { + "id": "speakleash/Bielik-11B-v3.0-Instruct", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 32000, + "output": 32000 + } + }, + "anthropic/claude-3-7-sonnet": { + "id": "anthropic/claude-3-7-sonnet", + "family": "claude-sonnet", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image", + "pdf" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 200000, + "output": 64000 + } + }, + "xai/grok-4-fast": { + "id": "xai/grok-4-fast", + "family": "grok", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 2000000, + "output": 64000 + } + }, + "pro/zai-org/glm-4.7": { + "id": "Pro/zai-org/GLM-4.7", + "family": "glm", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 205000, + "output": 205000 + } + }, + "pro/zai-org/glm-5": { + "id": "Pro/zai-org/GLM-5", + "family": "glm", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 205000, + "output": 205000 + } + }, + "pro/minimaxai/minimax-m2.5": { + "id": "Pro/MiniMaxAI/MiniMax-M2.5", + "family": "minimax", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 192000, + "output": 131000 + } + }, + "pro/minimaxai/minimax-m2.1": { + "id": "Pro/MiniMaxAI/MiniMax-M2.1", + "family": "minimax", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 197000, + "output": 131000 + } + }, + "pro/deepseek-ai/deepseek-r1": { + "id": "Pro/deepseek-ai/DeepSeek-R1", + "family": "deepseek-thinking", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 164000, + "output": 164000 + } + }, + "pro/deepseek-ai/deepseek-v3.2": { + "id": "Pro/deepseek-ai/DeepSeek-V3.2", + "family": "deepseek", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 164000, + "output": 164000 + } + }, + "pro/deepseek-ai/deepseek-v3": { + "id": "Pro/deepseek-ai/DeepSeek-V3", + "family": "deepseek", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 164000, + "output": 164000 + } + }, + "pro/deepseek-ai/deepseek-v3.1-terminus": { + "id": "Pro/deepseek-ai/DeepSeek-V3.1-Terminus", + "family": "deepseek", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 164000, + "output": 164000 + } + }, + "pro/moonshotai/kimi-k2-instruct-0905": { + "id": "Pro/moonshotai/Kimi-K2-Instruct-0905", + "family": "kimi", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 262000, + "output": 262000 + } + }, + "pro/moonshotai/kimi-k2.5": { + "id": "Pro/moonshotai/Kimi-K2.5", + "family": "kimi", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 262000, + "output": 262000 + } + }, + "pro/moonshotai/kimi-k2-thinking": { + "id": "Pro/moonshotai/Kimi-K2-Thinking", + "family": "kimi-thinking", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 262000, + "output": 262000 + } + }, + "paddlepaddle/paddleocr-vl-1.5": { + "id": "PaddlePaddle/PaddleOCR-VL-1.5", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 16384, + "output": 16384 + } + }, + "kwaipilot/kat-dev": { + "id": "Kwaipilot/KAT-Dev", + "family": "kat-coder", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 128000 + } + }, + "deepseek-ai/deepseek-ocr": { + "id": "deepseek-ai/DeepSeek-OCR", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 8192, + "output": 8192 + } + }, + "ascend-tribe/pangu-pro-moe": { + "id": "ascend-tribe/pangu-pro-moe", + "family": "pangu", + "reasoning": true, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 128000 + } + }, + "qwen/qwen3.5-9b": { + "id": "qwen/qwen3.5-9b", + "family": "qwen", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "image", + "text", + "video" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 256000, + "output": 32768 + } + }, + "qwen/qwen3.5-122b-a10b": { + "id": "qwen/qwen3.5-122b-a10b", + "family": "qwen", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "image", + "text", + "video" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 262144, + "output": 65536 + } + }, + "qwen/qwen3.5-35b-a3b": { + "id": "qwen/qwen3.5-35b-a3b", + "family": "qwen", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "image", + "text", + "video" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 262144, + "output": 65536 + } + }, + "qwen/qwen3.5-4b": { + "id": "Qwen/Qwen3.5-4B", + "family": "qwen", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image", + "video" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 262144, + "output": 65536 + } + }, + "qwen/qwen3.5-27b": { + "id": "qwen/qwen3.5-27b", + "family": "qwen", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "image", + "text", + "video" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 262144, + "output": 65536 + } + }, + "gpt-5-chat-latest": { + "id": "gpt-5-chat-latest", + "family": "gpt", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 400000, + "output": 128000, + "input": 272000 + } + }, + "llama-4-scout": { + "id": "llama-4-scout", + "family": "llama", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 131072, + "output": 8192 + } + }, + "codex-mini-latest": { + "id": "codex-mini-latest", + "family": "gpt-codex-mini", + "reasoning": true, + "temperature": false, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 200000, + "output": 100000 + } + }, + "qwen2.5-coder-7b-fast": { + "id": "qwen2.5-coder-7b-fast", + "family": "qwen", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 32000, + "output": 8192 + } + }, + "sonar-reasoning-pro": { + "id": "sonar-reasoning-pro", + "family": "sonar-reasoning", + "reasoning": true, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 127000, + "output": 128000, + "input": 127000 + } + }, + "llama-3.1-8b-instruct-turbo": { + "id": "llama-3.1-8b-instruct-turbo", + "family": "llama", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 128000 + } + }, + "ernie-4.5-21b-a3b-thinking": { + "id": "ernie-4.5-21b-a3b-thinking", + "family": "ernie", + "reasoning": true, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 8000 + } + }, + "llama-prompt-guard-2-22m": { + "id": "llama-prompt-guard-2-22m", + "family": "llama", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 512, + "output": 2 + } + }, + "gpt-4.1-mini-2025-04-14": { + "id": "gpt-4.1-mini-2025-04-14", + "family": "gpt-mini", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 1047576, + "output": 32768 + } + }, + "llama-guard-4": { + "id": "llama-guard-4", + "family": "llama", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 131072, + "output": 1024 + } + }, + "sonar-reasoning": { + "id": "sonar-reasoning", + "family": "sonar-reasoning", + "reasoning": true, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 127000, + "output": 4096 + } + }, + "deepseek-v3.1-terminus": { + "id": "deepseek-v3.1-terminus", + "family": "deepseek", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 16384 + } + }, + "claude-3.5-sonnet-v2": { + "id": "claude-3.5-sonnet-v2", + "family": "claude-sonnet", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 200000, + "output": 8192 + } + }, + "mistral-small": { + "id": "mistral-small", + "family": "mistral-small", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 128000 + } + }, + "qwen3-vl-235b-a22b-instruct": { + "id": "qwen3-vl-235b-a22b-instruct", + "family": "qwen", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image", + "video" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 256000, + "output": 16384 + } + }, + "qwen3-235b-a22b-thinking": { + "id": "qwen3-235b-a22b-thinking", + "family": "qwen", + "reasoning": true, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text", + "image", + "video" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 262144, + "output": 81920 + } + }, + "claude-3-haiku-20240307": { + "id": "claude-3-haiku-20240307", + "family": "claude-haiku", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image", + "pdf" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 200000, + "output": 4096 + } + }, + "kimi-k2-0711": { + "id": "kimi-k2-0711", + "family": "kimi", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 131072, + "output": 16384 + } + }, + "llama-4-maverick": { + "id": "llama-4-maverick", + "family": "llama", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 131072, + "output": 8192 + } + }, + "deepseek-tng-r1t2-chimera": { + "id": "deepseek-tng-r1t2-chimera", + "family": "deepseek-thinking", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 130000, + "output": 163840 + } + }, + "claude-opus-4": { + "id": "claude-opus-4", + "family": "claude-opus", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 200000, + "output": 32000 + } + }, + "llama-prompt-guard-2-86m": { + "id": "llama-prompt-guard-2-86m", + "family": "llama", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 512, + "output": 2 + } + }, + "gemma-3-12b-it": { + "id": "gemma-3-12b-it", + "family": "gemma", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 131072, + "output": 8192 + } + }, + "hermes-2-pro-llama-3-8b": { + "id": "hermes-2-pro-llama-3-8b", + "family": "llama", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 131072, + "output": 131072 + } + }, + "zai/glm-5": { + "id": "zai/glm-5", + "family": "glm", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 202800, + "output": 131072 + } + }, + "zai/glm-4.7-flashx": { + "id": "zai/glm-4.7-flashx", + "family": "glm-flash", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 200000, + "output": 128000 + } + }, + "zai/glm-4.5-air": { + "id": "zai/glm-4.5-air", + "family": "glm-air", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 96000 + } + }, + "zai/glm-4.5": { + "id": "zai/glm-4.5", + "family": "glm", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 131072, + "output": 131072 + } + }, + "zai/glm-4.7-flash": { + "id": "zai/glm-4.7-flash", + "family": "glm", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 200000, + "output": 131000 + } + }, + "zai/glm-4.6": { + "id": "zai/glm-4.6", + "family": "glm", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 200000, + "output": 96000 + } + }, + "zai/glm-4.7": { + "id": "zai/glm-4.7", + "family": "glm", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 202752, + "output": 120000 + } + }, + "zai/glm-4.6v-flash": { + "id": "zai/glm-4.6v-flash", + "family": "glm", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image", + "pdf" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 24000 + } + }, + "zai/glm-5-turbo": { + "id": "zai/glm-5-turbo", + "family": "glm", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 202800, + "output": 131100 + } + }, + "zai/glm-4.5v": { + "id": "zai/glm-4.5v", + "family": "glm", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 66000, + "output": 66000 + } + }, + "zai/glm-4.6v": { + "id": "zai/glm-4.6v", + "family": "glm", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image", + "pdf" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 24000 + } + }, + "nvidia/nemotron-nano-12b-v2-vl": { + "id": "nvidia/nemotron-nano-12b-v2-vl", + "family": "nemotron", + "reasoning": true, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "image", + "text", + "video" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 131072, + "output": 26215 + } + }, + "arcee-ai/trinity-large-preview": { + "id": "arcee-ai/trinity-large-preview", + "family": "trinity", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 131000, + "output": 131000 + } + }, + "arcee-ai/trinity-mini": { + "id": "arcee-ai/trinity-mini", + "family": "trinity-mini", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 131072, + "output": 8192, + "input": 131072 + } + }, + "inception/mercury-coder-small": { + "id": "inception/mercury-coder-small", + "family": "mercury", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 32000, + "output": 16384 + } + }, + "voyage/voyage-3-large": { + "id": "voyage/voyage-3-large", + "family": "voyage", + "reasoning": false, + "temperature": false, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 8192, + "output": 1536 + } + }, + "voyage/voyage-code-3": { + "id": "voyage/voyage-code-3", + "family": "voyage", + "reasoning": false, + "temperature": false, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 8192, + "output": 1536 + } + }, + "voyage/voyage-law-2": { + "id": "voyage/voyage-law-2", + "family": "voyage", + "reasoning": false, + "temperature": false, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 8192, + "output": 1536 + } + }, + "voyage/voyage-finance-2": { + "id": "voyage/voyage-finance-2", + "family": "voyage", + "reasoning": false, + "temperature": false, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 8192, + "output": 1536 + } + }, + "voyage/voyage-code-2": { + "id": "voyage/voyage-code-2", + "family": "voyage", + "reasoning": false, + "temperature": false, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 8192, + "output": 1536 + } + }, + "voyage/voyage-4-lite": { + "id": "voyage/voyage-4-lite", + "family": "voyage", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 32000, + "output": 0 + } + }, + "voyage/voyage-3.5-lite": { + "id": "voyage/voyage-3.5-lite", + "family": "voyage", + "reasoning": false, + "temperature": false, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 8192, + "output": 1536 + } + }, + "voyage/voyage-4-large": { + "id": "voyage/voyage-4-large", + "family": "voyage", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 32000, + "output": 0 + } + }, + "voyage/voyage-3.5": { + "id": "voyage/voyage-3.5", + "family": "voyage", + "reasoning": false, + "temperature": false, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 8192, + "output": 1536 + } + }, + "voyage/voyage-4": { + "id": "voyage/voyage-4", + "family": "voyage", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 32000, + "output": 0 + } + }, + "amazon/nova-2-lite": { + "id": "amazon/nova-2-lite", + "family": "nova", + "reasoning": true, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 1000000, + "output": 1000000 + } + }, + "amazon/titan-embed-text-v2": { + "id": "amazon/titan-embed-text-v2", + "family": "titan-embed", + "reasoning": false, + "temperature": false, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 8192, + "output": 1536 + } + }, + "amazon/nova-lite": { + "id": "amazon/nova-lite", + "family": "nova-lite", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image", + "video" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 300000, + "output": 8192 + } + }, + "amazon/nova-pro": { + "id": "amazon/nova-pro", + "family": "nova-pro", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image", + "video" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 300000, + "output": 8192 + } + }, + "amazon/nova-micro": { + "id": "amazon/nova-micro", + "family": "nova-micro", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 8192 + } + }, + "alibaba/qwen-3-235b": { + "id": "alibaba/qwen-3-235b", + "family": "qwen", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 40960, + "output": 16384 + } + }, + "alibaba/qwen3-max-preview": { + "id": "alibaba/qwen3-max-preview", + "family": "qwen", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 262144, + "output": 32768 + } + }, + "alibaba/qwen3-next-80b-a3b-thinking": { + "id": "alibaba/qwen3-next-80b-a3b-thinking", + "family": "qwen", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 131072, + "output": 65536 + } + }, + "alibaba/qwen3-max-thinking": { + "id": "alibaba/qwen3-max-thinking", + "family": "qwen", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 256000, + "output": 65536 + } + }, + "alibaba/qwen3-vl-instruct": { + "id": "alibaba/qwen3-vl-instruct", + "family": "qwen", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 131072, + "output": 129024 + } + }, + "alibaba/qwen3-embedding-8b": { + "id": "alibaba/qwen3-embedding-8b", + "family": "qwen", + "reasoning": false, + "temperature": false, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 32768, + "output": 32768 + } + }, + "alibaba/qwen3-coder-next": { + "id": "alibaba/qwen3-coder-next", + "family": "qwen", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 256000, + "output": 256000 + } + }, + "alibaba/qwen3-coder": { + "id": "alibaba/qwen3-coder", + "family": "qwen", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 262144, + "output": 66536 + } + }, + "alibaba/qwen-3-30b": { + "id": "alibaba/qwen-3-30b", + "family": "qwen", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 40960, + "output": 16384 + } + }, + "alibaba/qwen3-embedding-0.6b": { + "id": "alibaba/qwen3-embedding-0.6b", + "family": "qwen", + "reasoning": false, + "temperature": false, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 32768, + "output": 32768 + } + }, + "alibaba/qwen-3-14b": { + "id": "alibaba/qwen-3-14b", + "family": "qwen", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 40960, + "output": 16384 + } + }, + "alibaba/qwen3-235b-a22b-thinking": { + "id": "alibaba/qwen3-235b-a22b-thinking", + "family": "qwen", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image", + "pdf" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 262114, + "output": 262114 + } + }, + "alibaba/qwen3-vl-thinking": { + "id": "alibaba/qwen3-vl-thinking", + "family": "qwen", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 131072, + "output": 129024 + } + }, + "alibaba/qwen3.5-flash": { + "id": "alibaba/qwen3.5-flash", + "family": "qwen", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image", + "pdf" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 1000000, + "output": 64000 + } + }, + "alibaba/qwen3-next-80b-a3b-instruct": { + "id": "alibaba/qwen3-next-80b-a3b-instruct", + "family": "qwen", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 262144, + "output": 32768 + } + }, + "alibaba/qwen3.5-plus": { + "id": "alibaba/qwen3.5-plus", + "family": "qwen", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image", + "pdf" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 1000000, + "output": 64000 + } + }, + "alibaba/qwen3-max": { + "id": "alibaba/qwen3-max", + "family": "qwen", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 262144, + "output": 32768 + } + }, + "alibaba/qwen-3-32b": { + "id": "alibaba/qwen-3-32b", + "family": "qwen", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 40960, + "output": 16384 + } + }, + "alibaba/qwen3-coder-plus": { + "id": "alibaba/qwen3-coder-plus", + "family": "qwen", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 1000000, + "output": 1000000 + } + }, + "alibaba/qwen3-embedding-4b": { + "id": "alibaba/qwen3-embedding-4b", + "family": "qwen", + "reasoning": false, + "temperature": false, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 32768, + "output": 32768 + } + }, + "alibaba/qwen3-coder-30b-a3b": { + "id": "alibaba/qwen3-coder-30b-a3b", + "family": "qwen", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 160000, + "output": 32768 + } + }, + "bfl/flux-pro-1.0-fill": { + "id": "bfl/flux-pro-1.0-fill", + "family": "flux", + "reasoning": false, + "temperature": false, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "image" + ] + }, + "limit": { + "context": 512, + "output": 0 + } + }, + "bfl/flux-pro-1.1": { + "id": "bfl/flux-pro-1.1", + "family": "flux", + "reasoning": false, + "temperature": false, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "image" + ] + }, + "limit": { + "context": 512, + "output": 0 + } + }, + "bfl/flux-kontext-max": { + "id": "bfl/flux-kontext-max", + "family": "flux", + "reasoning": false, + "temperature": false, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "image" + ] + }, + "limit": { + "context": 512, + "output": 0 + } + }, + "bfl/flux-kontext-pro": { + "id": "bfl/flux-kontext-pro", + "family": "flux", + "reasoning": false, + "temperature": false, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "image" + ] + }, + "limit": { + "context": 512, + "output": 0 + } + }, + "bfl/flux-pro-1.1-ultra": { + "id": "bfl/flux-pro-1.1-ultra", + "family": "flux", + "reasoning": false, + "temperature": false, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "image" + ] + }, + "limit": { + "context": 512, + "output": 0 + } + }, + "mistral/codestral-embed": { + "id": "mistral/codestral-embed", + "family": "codestral-embed", + "reasoning": false, + "temperature": false, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 8192, + "output": 1536 + } + }, + "mistral/devstral-small-2": { + "id": "mistral/devstral-small-2", + "family": "devstral", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 256000, + "output": 256000 + } + }, + "mistral/devstral-2": { + "id": "mistral/devstral-2", + "family": "devstral", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 256000, + "output": 256000 + } + }, + "mistral/mistral-large-3": { + "id": "mistral/mistral-large-3", + "family": "mistral-large", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 256000, + "output": 256000 + } + }, + "mistral/mistral-embed": { + "id": "mistral/mistral-embed", + "family": "mistral-embed", + "reasoning": false, + "temperature": false, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 8192, + "output": 1536 + } + }, + "mistral/ministral-14b": { + "id": "mistral/ministral-14b", + "family": "ministral", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text", + "image", + "pdf" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 256000, + "output": 256000 + } + }, + "mistral/mistral-nemo": { + "id": "mistral/mistral-nemo", + "family": "mistral-nemo", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 60288, + "output": 16000 + } + }, + "mistral/mistral-medium": { + "id": "mistral/mistral-medium", + "family": "mistral-medium", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 64000 + } + }, + "mistral/devstral-small": { + "id": "mistral/devstral-small", + "family": "devstral", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 64000 + } + }, + "mistral/codestral": { + "id": "mistral/codestral", + "family": "codestral", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 256000, + "output": 4096 + } + }, + "mistral/mixtral-8x22b-instruct": { + "id": "mistral/mixtral-8x22b-instruct", + "family": "mixtral", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 64000, + "output": 64000 + } + }, + "mistral/mistral-small": { + "id": "mistral/mistral-small", + "family": "mistral-small", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 16384 + } + }, + "mistral/ministral-8b": { + "id": "mistral/ministral-8b", + "family": "ministral", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 128000 + } + }, + "mistral/pixtral-large": { + "id": "mistral/pixtral-large", + "family": "pixtral", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 128000 + } + }, + "mistral/pixtral-12b": { + "id": "mistral/pixtral-12b", + "family": "pixtral", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 128000 + } + }, + "mistral/magistral-small": { + "id": "mistral/magistral-small", + "family": "magistral-small", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 128000 + } + }, + "mistral/magistral-medium": { + "id": "mistral/magistral-medium", + "family": "magistral-medium", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 16384 + } + }, + "mistral/ministral-3b": { + "id": "mistral/ministral-3b", + "family": "ministral", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 128000 + } + }, + "kwaipilot/kat-coder-pro-v1": { + "id": "kwaipilot/kat-coder-pro-v1", + "family": "kat-coder", + "reasoning": true, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 256000, + "output": 32000 + } + }, + "deepseek/deepseek-v3": { + "id": "deepseek/deepseek-v3", + "family": "deepseek", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 163840, + "output": 16384 + } + }, + "deepseek/deepseek-v3.2-thinking": { + "id": "deepseek/deepseek-v3.2-thinking", + "family": "deepseek-thinking", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 64000 + } + }, + "moonshotai/kimi-k2-turbo": { + "id": "moonshotai/kimi-k2-turbo", + "family": "kimi", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 256000, + "output": 16384 + } + }, + "google/gemini-embedding-001": { + "id": "google/gemini-embedding-001", + "family": "gemini-embedding", + "reasoning": false, + "temperature": false, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 8192, + "output": 1536 + } + }, + "google/imagen-4.0-fast-generate-001": { + "id": "google/imagen-4.0-fast-generate-001", + "family": "imagen", + "reasoning": false, + "temperature": false, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "image" + ] + }, + "limit": { + "context": 480, + "output": 0 + } + }, + "google/text-embedding-005": { + "id": "google/text-embedding-005", + "family": "text-embedding", + "reasoning": false, + "temperature": false, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 8192, + "output": 1536 + } + }, + "google/imagen-4.0-ultra-generate-001": { + "id": "google/imagen-4.0-ultra-generate-001", + "family": "imagen", + "reasoning": false, + "temperature": false, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "image" + ] + }, + "limit": { + "context": 480, + "output": 0 + } + }, + "google/gemini-3.1-flash-image-preview": { + "id": "google/gemini-3.1-flash-image-preview", + "family": "gemini", + "reasoning": true, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "image", + "text" + ], + "output": [ + "image", + "text" + ] + }, + "limit": { + "context": 65536, + "output": 65536 + } + }, + "google/text-multilingual-embedding-002": { + "id": "google/text-multilingual-embedding-002", + "family": "text-embedding", + "reasoning": false, + "temperature": false, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 8192, + "output": 1536 + } + }, + "google/gemini-embedding-2": { + "id": "google/gemini-embedding-2", + "family": "gemini-embedding", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 0, + "output": 0 + } + }, + "google/gemini-2.5-flash-image": { + "id": "google/gemini-2.5-flash-image", + "family": "gemini-flash", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "image", + "text" + ], + "output": [ + "image", + "text" + ] + }, + "limit": { + "context": 32768, + "output": 32768 + } + }, + "google/gemini-3-pro-image": { + "id": "google/gemini-3-pro-image", + "family": "gemini-pro", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text", + "image" + ] + }, + "limit": { + "context": 65536, + "output": 32768 + } + }, + "google/gemini-2.5-flash-image-preview": { + "id": "google/gemini-2.5-flash-image-preview", + "family": "gemini-flash", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text", + "image" + ] + }, + "limit": { + "context": 32768, + "output": 32768 + } + }, + "google/imagen-4.0-generate-001": { + "id": "google/imagen-4.0-generate-001", + "family": "imagen", + "reasoning": false, + "temperature": false, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "image" + ] + }, + "limit": { + "context": 480, + "output": 0 + } + }, + "meituan/longcat-flash-thinking": { + "id": "meituan/longcat-flash-thinking", + "family": "longcat", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 8192 + } + }, + "meituan/longcat-flash-thinking-2601": { + "id": "meituan/longcat-flash-thinking-2601", + "family": "longcat", + "reasoning": true, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 32768, + "output": 32768 + } + }, + "bytedance/seed-1.6": { + "id": "bytedance/seed-1.6", + "family": "seed", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 256000, + "output": 32000 + } + }, + "bytedance/seed-1.8": { + "id": "bytedance/seed-1.8", + "family": "seed", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 256000, + "output": 64000 + } + }, + "meta/llama-3.1-8b": { + "id": "meta/llama-3.1-8b", + "family": "llama", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 131072, + "output": 16384 + } + }, + "meta/llama-3.2-11b": { + "id": "meta/llama-3.2-11b", + "family": "llama", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 8192 + } + }, + "meta/llama-3.1-70b": { + "id": "meta/llama-3.1-70b", + "family": "llama", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 131072, + "output": 16384 + } + }, + "meta/llama-3.2-90b": { + "id": "meta/llama-3.2-90b", + "family": "llama", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 8192 + } + }, + "meta/llama-3.2-1b": { + "id": "meta/llama-3.2-1b", + "family": "llama", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 8192 + } + }, + "meta/llama-3.2-3b": { + "id": "meta/llama-3.2-3b", + "family": "llama", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 8192 + } + }, + "meta/llama-4-maverick": { + "id": "meta/llama-4-maverick", + "family": "llama", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 4096 + } + }, + "meta/llama-3.3-70b": { + "id": "meta/llama-3.3-70b", + "family": "llama", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 4096 + } + }, + "meta/llama-4-scout": { + "id": "meta/llama-4-scout", + "family": "llama", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 4096 + } + }, + "vercel/v0-1.5-md": { + "id": "vercel/v0-1.5-md", + "family": "v0", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 32000 + } + }, + "vercel/v0-1.0-md": { + "id": "vercel/v0-1.0-md", + "family": "v0", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 32000 + } + }, + "openai/text-embedding-ada-002": { + "id": "openai/text-embedding-ada-002", + "family": "text-embedding", + "reasoning": false, + "temperature": false, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 8192, + "input": 6656, + "output": 1536 + } + }, + "openai/gpt-4o-mini-search-preview": { + "id": "openai/gpt-4o-mini-search-preview", + "family": "gpt-mini", + "reasoning": false, + "temperature": false, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "input": 128000, + "output": 16384 + } + }, + "openai/text-embedding-3-small": { + "id": "openai/text-embedding-3-small", + "family": "text-embedding", + "reasoning": false, + "temperature": false, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 8192, + "input": 6656, + "output": 1536 + } + }, + "openai/text-embedding-3-large": { + "id": "openai/text-embedding-3-large", + "family": "text-embedding", + "reasoning": false, + "temperature": false, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 8192, + "input": 6656, + "output": 1536 + } + }, + "openai/gpt-5.1-thinking": { + "id": "openai/gpt-5.1-thinking", + "family": "gpt", + "reasoning": true, + "temperature": false, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image", + "pdf" + ], + "output": [ + "text", + "image" + ] + }, + "limit": { + "context": 400000, + "input": 272000, + "output": 128000 + } + }, + "openai/codex-mini": { + "id": "openai/codex-mini", + "family": "gpt-codex-mini", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image", + "pdf" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 200000, + "input": 100000, + "output": 100000 + } + }, + "morph/morph-v3-large": { + "id": "morph/morph-v3-large", + "family": "morph", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 262144, + "output": 131072 + } + }, + "morph/morph-v3-fast": { + "id": "morph/morph-v3-fast", + "family": "morph", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 81920, + "output": 38000 + } + }, + "cohere/embed-v4.0": { + "id": "cohere/embed-v4.0", + "family": "cohere-embed", + "reasoning": false, + "temperature": false, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 8192, + "output": 1536 + } + }, + "cohere/command-a": { + "id": "cohere/command-a", + "family": "command", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 256000, + "output": 8192 + } + }, + "minimax/minimax-m2.1-lightning": { + "id": "minimax/minimax-m2.1-lightning", + "family": "minimax", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 204800, + "output": 131072 + } + }, + "recraft/recraft-v2": { + "id": "recraft/recraft-v2", + "family": "recraft", + "reasoning": false, + "temperature": false, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "image" + ] + }, + "limit": { + "context": 512, + "output": 0 + } + }, + "recraft/recraft-v3": { + "id": "recraft/recraft-v3", + "family": "recraft", + "reasoning": false, + "temperature": false, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "image" + ] + }, + "limit": { + "context": 512, + "output": 0 + } + }, + "perplexity/sonar-reasoning-pro": { + "id": "perplexity/sonar-reasoning-pro", + "family": "sonar-reasoning", + "reasoning": true, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 25600 + } + }, + "perplexity/sonar-reasoning": { + "id": "perplexity/sonar-reasoning", + "family": "sonar-reasoning", + "reasoning": true, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 127000, + "output": 8000 + } + }, + "perplexity/sonar-pro": { + "id": "perplexity/sonar-pro", + "family": "sonar-pro", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 200000, + "output": 8000 + } + }, + "anthropic/claude-3.5-sonnet-20240620": { + "id": "anthropic/claude-3.5-sonnet-20240620", + "family": "claude-sonnet", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image", + "pdf" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 200000, + "output": 8192 + } + }, + "xai/grok-4.20-non-reasoning-beta": { + "id": "xai/grok-4.20-non-reasoning-beta", + "family": "grok", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image", + "pdf" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 2000000, + "output": 2000000 + } + }, + "xai/grok-4.20-non-reasoning": { + "id": "xai/grok-4.20-non-reasoning", + "family": "grok", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image", + "pdf" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 2000000, + "output": 2000000 + } + }, + "xai/grok-imagine-image": { + "id": "xai/grok-imagine-image", + "family": "grok", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text", + "image" + ] + }, + "limit": { + "context": 0, + "output": 0 + } + }, + "xai/grok-4.20-reasoning": { + "id": "xai/grok-4.20-reasoning", + "family": "grok", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image", + "pdf" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 2000000, + "output": 2000000 + } + }, + "xai/grok-4.20-reasoning-beta": { + "id": "xai/grok-4.20-reasoning-beta", + "family": "grok", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image", + "pdf" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 2000000, + "output": 2000000 + } + }, + "xai/grok-4.20-multi-agent": { + "id": "xai/grok-4.20-multi-agent", + "family": "grok", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 2000000, + "output": 2000000 + } + }, + "xai/grok-imagine-image-pro": { + "id": "xai/grok-imagine-image-pro", + "family": "grok", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text", + "image" + ] + }, + "limit": { + "context": 0, + "output": 0 + } + }, + "xai/grok-4.20-multi-agent-beta": { + "id": "xai/grok-4.20-multi-agent-beta", + "family": "grok", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 2000000, + "output": 2000000 + } + }, + "xai/grok-3-fast": { + "id": "xai/grok-3-fast", + "family": "grok", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 131072, + "output": 8192 + } + }, + "xai/grok-3-mini-fast": { + "id": "xai/grok-3-mini-fast", + "family": "grok", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 131072, + "output": 8192 + } + }, + "xai/grok-2-vision": { + "id": "xai/grok-2-vision", + "family": "grok", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 8192, + "output": 4096 + } + }, + "gpt-4o-2024-05-13": { + "id": "gpt-4o-2024-05-13", + "family": "gpt", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 4096 + } + }, + "o3-deep-research": { + "id": "o3-deep-research", + "family": "o", + "reasoning": true, + "temperature": false, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 200000, + "output": 100000 + } + }, + "o4-mini-deep-research": { + "id": "o4-mini-deep-research", + "family": "o-mini", + "reasoning": true, + "temperature": false, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 200000, + "output": 100000 + } + }, + "gpt-3.5-turbo": { + "id": "gpt-3.5-turbo", + "family": "gpt", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 16385, + "output": 4096 + } + }, + "o1-pro": { + "id": "o1-pro", + "family": "o-pro", + "reasoning": true, + "temperature": false, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 200000, + "output": 100000 + } + }, + "gpt-5.2-pro": { + "id": "gpt-5.2-pro", + "family": "gpt-pro", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 400000, + "input": 272000, + "output": 128000 + } + }, + "gpt-4o-2024-08-06": { + "id": "gpt-4o-2024-08-06", + "family": "gpt", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 16384 + } + }, + "auto": { + "id": "auto", + "family": "auto", + "reasoning": false, + "temperature": false, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 32000, + "output": 32000 + } + }, + "morph-v3-fast": { + "id": "morph-v3-fast", + "family": "morph", + "reasoning": false, + "temperature": false, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 16000, + "output": 16000 + } + }, + "morph-v3-large": { + "id": "morph-v3-large", + "family": "morph", + "reasoning": false, + "temperature": false, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 32000, + "output": 32000 + } + }, + "c4ai-aya-expanse-32b": { + "id": "c4ai-aya-expanse-32b", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 4000 + } + }, + "command-a-03-2025": { + "id": "command-a-03-2025", + "family": "command-a", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 256000, + "output": 8000 + } + }, + "command-r7b-arabic-02-2025": { + "id": "command-r7b-arabic-02-2025", + "family": "command-r", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 4000 + } + }, + "command-a-translate-08-2025": { + "id": "command-a-translate-08-2025", + "family": "command-a", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 8000, + "output": 8000 + } + }, + "command-r-08-2024": { + "id": "command-r-08-2024", + "family": "command-r", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 4000 + } + }, + "command-r-plus-08-2024": { + "id": "command-r-plus-08-2024", + "family": "command-r", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 4000 + } + }, + "command-a-reasoning-08-2025": { + "id": "command-a-reasoning-08-2025", + "family": "command-a", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 256000, + "output": 8192, + "input": 256000 + } + }, + "c4ai-aya-expanse-8b": { + "id": "c4ai-aya-expanse-8b", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 8000, + "output": 4000 + } + }, + "c4ai-aya-vision-8b": { + "id": "c4ai-aya-vision-8b", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 16000, + "output": 4000 + } + }, + "c4ai-aya-vision-32b": { + "id": "c4ai-aya-vision-32b", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 16000, + "output": 4000 + } + }, + "command-r7b-12-2024": { + "id": "command-r7b-12-2024", + "family": "command-r", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 4000 + } + }, + "command-a-vision-07-2025": { + "id": "command-a-vision-07-2025", + "family": "command-a", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 8000 + } + }, + "v0-1.0-md": { + "id": "v0-1.0-md", + "family": "v0", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 200000, + "output": 64000, + "input": 200000 + } + }, + "v0-1.5-md": { + "id": "v0-1.5-md", + "family": "v0", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 200000, + "output": 64000, + "input": 200000 + } + }, + "v0-1.5-lg": { + "id": "v0-1.5-lg", + "family": "v0", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 1000000, + "output": 64000, + "input": 1000000 + } + }, + "llama-3_1-nemotron-ultra-253b-v1": { + "id": "Llama-3_1-Nemotron-Ultra-253B-v1", + "family": "llama", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 32000, + "output": 4096 + } + }, + "deepseek-r1-distill-qwen-32b": { + "id": "deepseek-r1-distill-qwen-32b", + "family": "qwen", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 32768, + "output": 16384 + } + }, + "glm-5-fp8": { + "id": "GLM-5-FP8", + "family": "glm", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 202000, + "output": 131072 + } + }, + "nvidia-nemotron-3-super-120b-a12b-nvfp4": { + "id": "NVIDIA-Nemotron-3-Super-120B-A12B-NVFP4", + "family": "nemotron", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 260000, + "output": 8192 + } + }, + "nvidia/nemotron-120b-a12b": { + "id": "nvidia/Nemotron-120B-A12B", + "family": "nemotron", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 262144, + "output": 32678 + } + }, + "claude-3-5-haiku-latest": { + "id": "claude-3-5-haiku-latest", + "family": "claude-haiku", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image", + "pdf" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 200000, + "output": 8192 + } + }, + "claude-3-5-sonnet-20241022": { + "id": "claude-3-5-sonnet-20241022", + "family": "claude-sonnet", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 200000, + "output": 8192, + "input": 200000 + } + }, + "claude-3-sonnet-20240229": { + "id": "claude-3-sonnet-20240229", + "family": "claude-sonnet", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image", + "pdf" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 200000, + "output": 4096 + } + }, + "claude-sonnet-4-0": { + "id": "claude-sonnet-4-0", + "family": "claude-sonnet", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image", + "pdf" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 200000, + "output": 64000 + } + }, + "claude-opus-4-0": { + "id": "claude-opus-4-0", + "family": "claude-opus", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image", + "pdf" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 200000, + "output": 32000 + } + }, + "claude-3-5-haiku-20241022": { + "id": "claude-3-5-haiku-20241022", + "family": "claude-haiku", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image", + "pdf" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 200000, + "output": 8192, + "input": 200000 + } + }, + "claude-3-5-sonnet-20240620": { + "id": "claude-3-5-sonnet-20240620", + "family": "claude-sonnet", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image", + "pdf" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 200000, + "output": 8192, + "input": 200000 + } + }, + "claude-3-7-sonnet-latest": { + "id": "claude-3-7-sonnet-latest", + "family": "claude-sonnet", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image", + "pdf" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 200000, + "output": 64000 + } + }, + "claude-3-opus-20240229": { + "id": "claude-3-opus-20240229", + "family": "claude-opus", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image", + "pdf" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 200000, + "output": 4096 + } + }, + "hunyuan-turbos": { + "id": "hunyuan-turbos", + "family": "hunyuan", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 131072, + "output": 16384 + } + }, + "tc-code-latest": { + "id": "tc-code-latest", + "family": "auto", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 131072, + "output": 16384 + } + }, + "hunyuan-t1": { + "id": "hunyuan-t1", + "family": "hunyuan", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 131072, + "output": 16384 + } + }, + "hunyuan-2.0-instruct": { + "id": "hunyuan-2.0-instruct", + "family": "hunyuan", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 131072, + "output": 16384 + } + }, + "hunyuan-2.0-thinking": { + "id": "hunyuan-2.0-thinking", + "family": "hunyuan", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 131072, + "output": 16384 + } + }, + "claude-sonnet-4-5@20250929": { + "id": "claude-sonnet-4-5@20250929", + "family": "claude-sonnet", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image", + "pdf" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 200000, + "output": 64000 + } + }, + "claude-opus-4-1@20250805": { + "id": "claude-opus-4-1@20250805", + "family": "claude-opus", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image", + "pdf" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 200000, + "output": 32000 + } + }, + "claude-3-7-sonnet@20250219": { + "id": "claude-3-7-sonnet@20250219", + "family": "claude-sonnet", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image", + "pdf" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 200000, + "output": 64000 + } + }, + "claude-opus-4@20250514": { + "id": "claude-opus-4@20250514", + "family": "claude-opus", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image", + "pdf" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 200000, + "output": 32000 + } + }, + "claude-opus-4-5@20251101": { + "id": "claude-opus-4-5@20251101", + "family": "claude-opus", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image", + "pdf" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 200000, + "output": 64000 + } + }, + "claude-3-5-haiku@20241022": { + "id": "claude-3-5-haiku@20241022", + "family": "claude-haiku", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image", + "pdf" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 200000, + "output": 8192 + } + }, + "claude-sonnet-4@20250514": { + "id": "claude-sonnet-4@20250514", + "family": "claude-sonnet", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image", + "pdf" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 200000, + "output": 64000 + } + }, + "claude-3-5-sonnet@20241022": { + "id": "claude-3-5-sonnet@20241022", + "family": "claude-sonnet", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image", + "pdf" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 200000, + "output": 8192 + } + }, + "claude-opus-4-6@default": { + "id": "claude-opus-4-6@default", + "family": "claude-opus", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image", + "pdf" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 1000000, + "output": 128000 + } + }, + "claude-haiku-4-5@20251001": { + "id": "claude-haiku-4-5@20251001", + "family": "claude-haiku", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image", + "pdf" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 200000, + "output": 64000 + } + }, + "claude-sonnet-4-6@default": { + "id": "claude-sonnet-4-6@default", + "family": "claude-sonnet", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image", + "pdf" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 200000, + "output": 64000 + } + }, + "giga-potato-thinking": { + "id": "giga-potato-thinking", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "image", + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 256000, + "output": 32000 + } + }, + "corethink:free": { + "id": "corethink:free", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 78000, + "output": 8192 + } + }, + "morph-warp-grep-v2": { + "id": "morph-warp-grep-v2", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 256000, + "output": 32000 + } + }, + "giga-potato": { + "id": "giga-potato", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "image", + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 256000, + "output": 32000 + } + }, + "allenai/olmo-2-0325-32b-instruct": { + "id": "allenai/olmo-2-0325-32b-instruct", + "reasoning": false, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 32768 + } + }, + "allenai/olmo-3-7b-instruct": { + "id": "allenai/olmo-3-7b-instruct", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 65536, + "output": 65536 + } + }, + "allenai/olmo-3-32b-think": { + "id": "allenai/olmo-3-32b-think", + "reasoning": true, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 8192, + "input": 128000 + }, + "family": "allenai" + }, + "allenai/molmo-2-8b": { + "id": "allenai/molmo-2-8b", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 36864, + "output": 36864, + "input": 36864 + }, + "family": "allenai" + }, + "allenai/olmo-3.1-32b-instruct": { + "id": "allenai/olmo-3.1-32b-instruct", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 65536, + "output": 8192, + "input": 65536 + }, + "family": "allenai" + }, + "allenai/olmo-3-7b-think": { + "id": "allenai/olmo-3-7b-think", + "reasoning": true, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 65536, + "output": 65536 + } + }, + "allenai/olmo-3.1-32b-think": { + "id": "allenai/olmo-3.1-32b-think", + "reasoning": true, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 65536, + "output": 8192, + "input": 65536 + }, + "family": "allenai" + }, + "nvidia/nemotron-3-super-120b-a12b:free": { + "id": "nvidia/nemotron-3-super-120b-a12b:free", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 262144, + "output": 262144 + } + }, + "ibm-granite/granite-4.0-h-micro": { + "id": "ibm-granite/granite-4.0-h-micro", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 131000, + "output": 32768 + } + }, + "arcee-ai/coder-large": { + "id": "arcee-ai/coder-large", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 32768, + "output": 32768 + } + }, + "arcee-ai/virtuoso-large": { + "id": "arcee-ai/virtuoso-large", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 131072, + "output": 64000 + } + }, + "arcee-ai/maestro-reasoning": { + "id": "arcee-ai/maestro-reasoning", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 131072, + "output": 32000 + } + }, + "arcee-ai/spotlight": { + "id": "arcee-ai/spotlight", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "image", + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 131072, + "output": 65537 + } + }, + "alfredpros/codellama-7b-instruct-solidity": { + "id": "alfredpros/codellama-7b-instruct-solidity", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 4096, + "output": 4096 + } + }, + "liquid/lfm-2.2-6b": { + "id": "liquid/lfm-2.2-6b", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 32768, + "output": 32768 + } + }, + "liquid/lfm-2-24b-a2b": { + "id": "liquid/lfm-2-24b-a2b", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 32768, + "output": 32768 + } + }, + "liquid/lfm2-8b-a1b": { + "id": "liquid/lfm2-8b-a1b", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 32768, + "output": 32768 + } + }, + "upstage/solar-pro-3": { + "id": "upstage/solar-pro-3", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 32768 + } + }, + "switchpoint/router": { + "id": "switchpoint/router", + "reasoning": true, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 131072, + "output": 32768 + } + }, + "kilo-auto/balanced": { + "id": "kilo-auto/balanced", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 204800, + "output": 131072 + } + }, + "kilo-auto/free": { + "id": "kilo-auto/free", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 204800, + "output": 131072 + } + }, + "kilo-auto/small": { + "id": "kilo-auto/small", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "image", + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 400000, + "output": 128000 + } + }, + "kilo-auto/frontier": { + "id": "kilo-auto/frontier", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "image", + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 1000000, + "output": 128000 + } + }, + "amazon/nova-micro-v1": { + "id": "amazon/nova-micro-v1", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 5120, + "input": 128000 + }, + "family": "nova-micro" + }, + "amazon/nova-lite-v1": { + "id": "amazon/nova-lite-v1", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 300000, + "output": 5120, + "input": 300000 + }, + "family": "nova-lite" + }, + "amazon/nova-premier-v1": { + "id": "amazon/nova-premier-v1", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "image", + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 1000000, + "output": 32000 + } + }, + "amazon/nova-2-lite-v1": { + "id": "amazon/nova-2-lite-v1", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 1000000, + "output": 65535, + "input": 1000000 + }, + "family": "nova" + }, + "amazon/nova-pro-v1": { + "id": "amazon/nova-pro-v1", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 300000, + "output": 32000, + "input": 300000 + }, + "family": "nova-pro" + }, + "anthracite-org/magnum-v4-72b": { + "id": "anthracite-org/magnum-v4-72b", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text", + "pdf" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 16384, + "output": 8192, + "input": 16384 + }, + "family": "llama" + }, + "alibaba/tongyi-deepresearch-30b-a3b": { + "id": "alibaba/tongyi-deepresearch-30b-a3b", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 131072, + "output": 131072 + } + }, + "aion-labs/aion-1.0-mini": { + "id": "aion-labs/aion-1.0-mini", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 131072, + "output": 8192, + "input": 131072 + }, + "family": "deepseek" + }, + "aion-labs/aion-2.0": { + "id": "aion-labs/aion-2.0", + "reasoning": true, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 131072, + "output": 32768 + } + }, + "aion-labs/aion-rp-llama-3.1-8b": { + "id": "aion-labs/aion-rp-llama-3.1-8b", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 32768, + "output": 16384, + "input": 32768 + }, + "family": "llama" + }, + "aion-labs/aion-1.0": { + "id": "aion-labs/aion-1.0", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 65536, + "output": 8192, + "input": 65536 + }, + "family": "llama" + }, + "relace/relace-search": { + "id": "relace/relace-search", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 256000, + "output": 128000 + } + }, + "relace/relace-apply-3": { + "id": "relace/relace-apply-3", + "reasoning": false, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 256000, + "output": 128000 + } + }, + "thedrummer/rocinante-12b": { + "id": "thedrummer/rocinante-12b", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 32768, + "output": 32768 + } + }, + "thedrummer/cydonia-24b-v4.1": { + "id": "thedrummer/cydonia-24b-v4.1", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 131072, + "output": 131072 + } + }, + "thedrummer/unslopnemo-12b": { + "id": "thedrummer/unslopnemo-12b", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 32768, + "output": 32768 + } + }, + "thedrummer/skyfall-36b-v2": { + "id": "thedrummer/skyfall-36b-v2", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 32768, + "output": 32768 + } + }, + "mancer/weaver": { + "id": "mancer/weaver", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 8000, + "output": 2000 + } + }, + "deepseek/deepseek-r1-distill-qwen-32b": { + "id": "deepseek/deepseek-r1-distill-qwen-32b", + "reasoning": true, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 32768, + "output": 32768 + } + }, + "alpindale/goliath-120b": { + "id": "alpindale/goliath-120b", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 6144, + "output": 1024 + } + }, + "openrouter/hunter-alpha": { + "id": "openrouter/hunter-alpha", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 1048576, + "output": 32000 + } + }, + "openrouter/auto": { + "id": "openrouter/auto", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "audio", + "image", + "pdf", + "text", + "video" + ], + "output": [ + "image", + "text" + ] + }, + "limit": { + "context": 2000000, + "output": 32768 + } + }, + "openrouter/healer-alpha": { + "id": "openrouter/healer-alpha", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "audio", + "image", + "text", + "video" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 262144, + "output": 32000 + } + }, + "openrouter/bodybuilder": { + "id": "openrouter/bodybuilder", + "reasoning": false, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 32768 + } + }, + "google/gemini-2.5-pro-preview": { + "id": "google/gemini-2.5-pro-preview", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "audio", + "image", + "pdf", + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 1048576, + "output": 65536 + } + }, + "google/gemini-2.0-flash-lite-001": { + "id": "google/gemini-2.0-flash-lite-001", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "audio", + "image", + "pdf", + "text", + "video" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 1048576, + "output": 8192 + } + }, + "z-ai/glm-4-32b": { + "id": "z-ai/glm-4-32b", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 32768 + } + }, + "deepcogito/cogito-v2.1-671b": { + "id": "deepcogito/cogito-v2.1-671b", + "reasoning": true, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 16384, + "input": 128000 + }, + "family": "cogito" + }, + "bytedance/ui-tars-1.5-7b": { + "id": "bytedance/ui-tars-1.5-7b", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "image", + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 2048 + } + }, + "undi95/remm-slerp-l2-13b": { + "id": "undi95/remm-slerp-l2-13b", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text", + "pdf" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 6144, + "output": 4096, + "input": 6144 + }, + "family": "llama" + }, + "qwen/qwen-vl-plus": { + "id": "qwen/qwen-vl-plus", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "image", + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 131072, + "output": 8192 + } + }, + "qwen/qwen-vl-max": { + "id": "qwen/qwen-vl-max", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 131072, + "output": 32768 + } + }, + "qwen/qwen-2.5-vl-7b-instruct": { + "id": "qwen/qwen-2.5-vl-7b-instruct", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 32768, + "output": 6554 + } + }, + "qwen/qwen3-max-thinking": { + "id": "qwen/qwen3-max-thinking", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 262144, + "output": 32768 + } + }, + "qwen/qwen-max": { + "id": "qwen/qwen-max", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 32768, + "output": 8192 + } + }, + "qwen/qwen-turbo": { + "id": "qwen/qwen-turbo", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 131072, + "output": 8192 + } + }, + "qwen/qwen3-235b-a22b-2507": { + "id": "qwen/qwen3-235b-a22b-2507", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 262144, + "output": 52429 + } + }, + "qwen/qwen-2.5-7b-instruct": { + "id": "qwen/qwen-2.5-7b-instruct", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 32768, + "output": 6554 + } + }, + "qwen/qwen-plus": { + "id": "qwen/qwen-plus", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 1000000, + "output": 32768 + } + }, + "qwen/qwen-plus-2025-07-28": { + "id": "qwen/qwen-plus-2025-07-28", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 1000000, + "output": 32768 + } + }, + "qwen/qwen3-30b-a3b": { + "id": "Qwen/Qwen3-30B-A3B", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 40960, + "output": 40960 + }, + "family": "qwen" + }, + "qwen/qwen-plus-2025-07-28:thinking": { + "id": "qwen/qwen-plus-2025-07-28:thinking", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 1000000, + "output": 32768 + } + }, + "qwen/qwen3.5-flash-02-23": { + "id": "qwen/qwen3.5-flash-02-23", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "image", + "text", + "video" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 1000000, + "output": 65536 + } + }, + "eleutherai/llemma_7b": { + "id": "eleutherai/llemma_7b", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 4096, + "output": 4096 + } + }, + "x-ai/grok-code-fast-1:optimized:free": { + "id": "x-ai/grok-code-fast-1:optimized:free", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 256000, + "output": 10000 + } + }, + "meta-llama/llama-4-scout": { + "id": "meta-llama/llama-4-scout", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 328000, + "output": 65536, + "input": 328000 + }, + "family": "llama" + }, + "meta-llama/llama-3.2-3b-instruct": { + "id": "meta-llama/llama-3.2-3b-instruct", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text", + "pdf" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 131072, + "output": 8192, + "input": 131072 + }, + "family": "llama" + }, + "meta-llama/llama-3.2-1b-instruct": { + "id": "meta-llama/llama-3.2-1b-instruct", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 60000, + "output": 12000 + } + }, + "meta-llama/llama-3.1-405b-instruct": { + "id": "meta-llama/llama-3.1-405b-instruct", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 131000, + "output": 26200 + } + }, + "meta-llama/llama-4-maverick": { + "id": "meta-llama/llama-4-maverick", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 1048576, + "output": 65536, + "input": 1048576 + }, + "family": "llama" + }, + "meta-llama/llama-3.1-405b": { + "id": "meta-llama/llama-3.1-405b", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 32768, + "output": 32768 + } + }, + "tngtech/deepseek-r1t2-chimera": { + "id": "tngtech/deepseek-r1t2-chimera", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 163840, + "output": 163840 + } + }, + "mistralai/ministral-3b-2512": { + "id": "mistralai/ministral-3b-2512", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 131072, + "output": 32768, + "input": 131072 + }, + "family": "ministral" + }, + "mistralai/mistral-saba": { + "id": "mistralai/mistral-saba", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 32000, + "output": 32768, + "input": 32000 + }, + "family": "mistral" + }, + "mistralai/mistral-small-24b-instruct-2501": { + "id": "mistralai/mistral-small-24b-instruct-2501", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 32768, + "output": 16384 + } + }, + "mistralai/pixtral-large-2411": { + "id": "mistralai/pixtral-large-2411", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "image", + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 131072, + "output": 32768 + } + }, + "mistralai/mistral-small-creative": { + "id": "mistralai/mistral-small-creative", + "reasoning": false, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 32768, + "output": 32768, + "input": 32768 + }, + "family": "mistral-small" + }, + "mistralai/mistral-large-2512": { + "id": "mistralai/mistral-large-2512", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 262144, + "output": 52429 + } + }, + "mistralai/ministral-8b-2512": { + "id": "mistralai/ministral-8b-2512", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 262144, + "output": 32768, + "input": 262144 + }, + "family": "ministral" + }, + "mistralai/ministral-14b-2512": { + "id": "mistralai/ministral-14b-2512", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 262144, + "output": 32768, + "input": 262144 + }, + "family": "ministral" + }, + "mistralai/devstral-medium": { + "id": "mistralai/devstral-medium", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 131072, + "output": 26215 + } + }, + "mistralai/mistral-large-2407": { + "id": "mistralai/mistral-large-2407", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 131072, + "output": 32768 + } + }, + "mistralai/devstral-small": { + "id": "mistralai/devstral-small", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 131072, + "output": 26215 + } + }, + "mistralai/mixtral-8x22b-instruct": { + "id": "mistralai/mixtral-8x22b-instruct", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 65536, + "output": 13108 + } + }, + "mistralai/mistral-large-2411": { + "id": "mistralai/mistral-large-2411", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 131072, + "output": 26215 + } + }, + "mistralai/mistral-7b-instruct-v0.1": { + "id": "mistralai/mistral-7b-instruct-v0.1", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 2824, + "output": 565 + } + }, + "mistralai/mistral-large": { + "id": "mistralai/mistral-large", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 256000, + "input": 128000 + }, + "family": "mistral-large" + }, + "mistralai/mixtral-8x7b-instruct": { + "id": "mistralai/mixtral-8x7b-instruct", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 32768, + "output": 16384 + } + }, + "openai/gpt-4o-2024-11-20": { + "id": "openai/gpt-4o-2024-11-20", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 16384, + "input": 128000 + }, + "family": "gpt" + }, + "openai/gpt-4o:extended": { + "id": "openai/gpt-4o:extended", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "image", + "pdf", + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 64000 + } + }, + "openai/gpt-4o-2024-05-13": { + "id": "openai/gpt-4o-2024-05-13", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "image", + "pdf", + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 4096 + } + }, + "openai/gpt-4o-audio-preview": { + "id": "openai/gpt-4o-audio-preview", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "audio", + "text" + ], + "output": [ + "audio", + "text" + ] + }, + "limit": { + "context": 128000, + "output": 16384 + } + }, + "openai/gpt-4o-mini-2024-07-18": { + "id": "openai/gpt-4o-mini-2024-07-18", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "image", + "pdf", + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 16384 + } + }, + "openai/gpt-audio": { + "id": "openai/gpt-audio", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "audio", + "text" + ], + "output": [ + "audio", + "text" + ] + }, + "limit": { + "context": 128000, + "output": 16384 + } + }, + "openai/gpt-3.5-turbo-16k": { + "id": "openai/gpt-3.5-turbo-16k", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 16385, + "output": 4096 + } + }, + "openai/gpt-5-image-mini": { + "id": "openai/gpt-5-image-mini", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "image", + "pdf", + "text" + ], + "output": [ + "image", + "text" + ] + }, + "limit": { + "context": 400000, + "output": 128000 + } + }, + "openai/gpt-4-turbo-preview": { + "id": "openai/gpt-4-turbo-preview", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 4096, + "input": 128000 + }, + "family": "gpt" + }, + "openai/gpt-3.5-turbo-0613": { + "id": "openai/gpt-3.5-turbo-0613", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 4095, + "output": 4096 + } + }, + "openai/gpt-4-0314": { + "id": "openai/gpt-4-0314", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 8191, + "output": 4096 + } + }, + "openai/gpt-audio-mini": { + "id": "openai/gpt-audio-mini", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "audio", + "text" + ], + "output": [ + "audio", + "text" + ] + }, + "limit": { + "context": 128000, + "output": 16384 + } + }, + "openai/gpt-4-1106-preview": { + "id": "openai/gpt-4-1106-preview", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 4096 + } + }, + "openai/gpt-4o-2024-08-06": { + "id": "openai/gpt-4o-2024-08-06", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 16384, + "input": 128000 + }, + "family": "gpt" + }, + "openai/o4-mini-high": { + "id": "openai/o4-mini-high", + "reasoning": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 200000, + "output": 100000, + "input": 200000 + }, + "family": "o-mini" + }, + "openai/gpt-4o-search-preview": { + "id": "openai/gpt-4o-search-preview", + "reasoning": false, + "toolCall": false, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 16384, + "input": 128000 + }, + "family": "gpt" + }, + "cohere/command-r-08-2024": { + "id": "cohere/command-r-08-2024", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 4000 + } + }, + "cohere/command-r-plus-08-2024": { + "id": "cohere/command-r-plus-08-2024", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 4096, + "input": 128000 + }, + "family": "command-r" + }, + "cohere/command-r7b-12-2024": { + "id": "cohere/command-r7b-12-2024", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 4000 + } + }, + "minimax/minimax-m2-her": { + "id": "minimax/minimax-m2-her", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 65532, + "output": 2048, + "input": 65532 + }, + "family": "minimax" + }, + "minimax/minimax-m2.5:free": { + "id": "minimax/minimax-m2.5:free", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 204800, + "output": 131072 + } + }, + "sao10k/l3.1-70b-hanami-x1": { + "id": "Sao10K/L3.1-70B-Hanami-x1", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 16384, + "output": 16384, + "input": 16384 + }, + "family": "llama" + }, + "sao10k/l3-lunaris-8b": { + "id": "sao10k/l3-lunaris-8b", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 8192, + "output": 8192 + } + }, + "sao10k/l3.1-euryale-70b": { + "id": "sao10k/l3.1-euryale-70b", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 131072, + "output": 16384 + } + }, + "sao10k/l3-euryale-70b": { + "id": "sao10k/l3-euryale-70b", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 8192, + "output": 8192 + } + }, + "sao10k/l3.3-euryale-70b": { + "id": "sao10k/l3.3-euryale-70b", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 131072, + "output": 16384 + } + }, + "writer/palmyra-x5": { + "id": "writer/palmyra-x5", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 1040000, + "output": 8192 + } + }, + "perplexity/sonar-deep-research": { + "id": "perplexity/sonar-deep-research", + "reasoning": true, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 25600 + } + }, + "perplexity/sonar-pro-search": { + "id": "perplexity/sonar-pro-search", + "reasoning": true, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "image", + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 200000, + "output": 8000 + } + }, + "bytedance-seed/seed-2.0-mini": { + "id": "bytedance-seed/seed-2.0-mini", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "image", + "text", + "video" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 262144, + "output": 131072 + } + }, + "bytedance-seed/seed-1.6": { + "id": "bytedance-seed/seed-1.6", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "image", + "text", + "video" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 262144, + "output": 32768 + } + }, + "bytedance-seed/seed-1.6-flash": { + "id": "bytedance-seed/seed-1.6-flash", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "image", + "text", + "video" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 262144, + "output": 32768 + } + }, + "bytedance-seed/seed-2.0-lite": { + "id": "bytedance-seed/seed-2.0-lite", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "image", + "text", + "video" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 262144, + "output": 131072 + } + }, + "anthropic/claude-3.7-sonnet:thinking": { + "id": "anthropic/claude-3.7-sonnet:thinking", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "image", + "pdf", + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 200000, + "output": 64000 + } + }, + "ai21/jamba-large-1.7": { + "id": "ai21/jamba-large-1.7", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 256000, + "output": 4096 + } + }, + "kilo/auto": { + "id": "kilo/auto", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "image", + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 1000000, + "output": 128000 + } + }, + "kilo/auto-free": { + "id": "kilo/auto-free", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 204800, + "output": 131072 + } + }, + "kilo/auto-small": { + "id": "kilo/auto-small", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "image", + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 400000, + "output": 128000 + } + }, + "inflection/inflection-3-productivity": { + "id": "inflection/inflection-3-productivity", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 8000, + "output": 4096, + "input": 8000 + }, + "family": "gpt" + }, + "inflection/inflection-3-pi": { + "id": "inflection/inflection-3-pi", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 8000, + "output": 4096, + "input": 8000 + }, + "family": "gpt" + }, + "nousresearch/hermes-3-llama-3.1-70b": { + "id": "nousresearch/hermes-3-llama-3.1-70b", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 131072, + "output": 32768 + } + }, + "nousresearch/hermes-3-llama-3.1-405b": { + "id": "nousresearch/hermes-3-llama-3.1-405b", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 131072, + "output": 16384 + } + }, + "exa-research-pro": { + "id": "exa-research-pro", + "reasoning": false, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 16384, + "input": 16384, + "output": 16384 + } + }, + "gemini-2.0-pro-exp-02-05": { + "id": "gemini-2.0-pro-exp-02-05", + "reasoning": false, + "toolCall": false, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 2097152, + "input": 2097152, + "output": 8192 + } + }, + "qwen-image": { + "id": "qwen-image", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "image" + ] + }, + "limit": { + "context": 0, + "output": 0 + } + }, + "llama-3.3-70b-shakudo": { + "id": "Llama-3.3-70B-Shakudo", + "reasoning": false, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 32768, + "input": 32768, + "output": 16384 + } + }, + "ernie-4.5-8k-preview": { + "id": "ernie-4.5-8k-preview", + "reasoning": false, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 8000, + "input": 8000, + "output": 16384 + } + }, + "claude-3-7-sonnet-thinking:128000": { + "id": "claude-3-7-sonnet-thinking:128000", + "reasoning": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image", + "pdf" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 200000, + "input": 200000, + "output": 64000 + } + }, + "phi-4-multimodal-instruct": { + "id": "phi-4-multimodal-instruct", + "reasoning": false, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "input": 128000, + "output": 16384 + } + }, + "z-image-turbo": { + "id": "z-image-turbo", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "image" + ] + }, + "limit": { + "context": 0, + "output": 0 + } + }, + "llama-3.3+(3v3.3)-70b-tenyxchat-daybreakstorywriter": { + "id": "Llama-3.3+(3v3.3)-70B-TenyxChat-DaybreakStorywriter", + "reasoning": false, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 32768, + "input": 32768, + "output": 16384 + } + }, + "mistral-small-31-24b-instruct": { + "id": "mistral-small-31-24b-instruct", + "reasoning": false, + "toolCall": false, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "input": 128000, + "output": 131072 + } + }, + "llama-3.3-70b-the-omega-directive-unslop-v2.0": { + "id": "Llama-3.3-70B-The-Omega-Directive-Unslop-v2.0", + "reasoning": false, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 32768, + "input": 32768, + "output": 16384 + } + }, + "baichuan-m2": { + "id": "Baichuan-M2", + "reasoning": false, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 32768, + "input": 32768, + "output": 32768 + } + }, + "doubao-1.5-vision-pro-32k": { + "id": "doubao-1.5-vision-pro-32k", + "reasoning": false, + "toolCall": false, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 32000, + "input": 32000, + "output": 8192 + } + }, + "glm-4.5-air-derestricted-iceblink-v2-reextract": { + "id": "GLM-4.5-Air-Derestricted-Iceblink-v2-ReExtract", + "reasoning": false, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 131072, + "input": 131072, + "output": 65536 + } + }, + "llama-3.3-70b-arliai-rpmax-v1.4": { + "id": "Llama-3.3-70B-ArliAI-RPMax-v1.4", + "reasoning": false, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 32768, + "input": 32768, + "output": 16384 + } + }, + "jamba-large-1.6": { + "id": "jamba-large-1.6", + "reasoning": false, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 256000, + "input": 256000, + "output": 4096 + } + }, + "llama-3.3-70b-aurora-borealis": { + "id": "Llama-3.3-70B-Aurora-Borealis", + "reasoning": false, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 32768, + "input": 32768, + "output": 16384 + } + }, + "ernie-x1-32k": { + "id": "ernie-x1-32k", + "reasoning": false, + "toolCall": false, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 32000, + "input": 32000, + "output": 16384 + } + }, + "llama-3.3-70b-magnum-v4-se": { + "id": "Llama-3.3-70B-Magnum-v4-SE", + "reasoning": false, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 32768, + "input": 32768, + "output": 16384 + } + }, + "kat-coder-pro-v1": { + "id": "KAT-Coder-Pro-V1", + "reasoning": false, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 256000, + "input": 256000, + "output": 32768 + } + }, + "hunyuan-turbos-20250226": { + "id": "hunyuan-turbos-20250226", + "reasoning": false, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 24000, + "input": 24000, + "output": 8192 + } + }, + "jamba-large-1.7": { + "id": "jamba-large-1.7", + "reasoning": false, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 256000, + "input": 256000, + "output": 4096 + } + }, + "mercury-coder-small": { + "id": "mercury-coder-small", + "reasoning": false, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 32768, + "input": 32768, + "output": 16384 + } + }, + "doubao-1-5-thinking-pro-vision-250415": { + "id": "doubao-1-5-thinking-pro-vision-250415", + "reasoning": false, + "toolCall": false, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "input": 128000, + "output": 16384 + } + }, + "yi-medium-200k": { + "id": "yi-medium-200k", + "reasoning": false, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 200000, + "input": 200000, + "output": 4096 + } + }, + "deepseek-chat-cheaper": { + "id": "deepseek-chat-cheaper", + "reasoning": false, + "toolCall": true, + "modalities": { + "input": [ + "text", + "pdf" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "input": 128000, + "output": 8192 + } + }, + "step-r1-v-mini": { + "id": "step-r1-v-mini", + "reasoning": false, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "input": 128000, + "output": 65536 + } + }, + "yi-lightning": { + "id": "yi-lightning", + "reasoning": false, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 12000, + "input": 12000, + "output": 4096 + } + }, + "deepseek-reasoner-cheaper": { + "id": "deepseek-reasoner-cheaper", + "reasoning": false, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "input": 128000, + "output": 65536 + } + }, + "ernie-4.5-turbo-vl-32k": { + "id": "ernie-4.5-turbo-vl-32k", + "reasoning": false, + "toolCall": false, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 32000, + "input": 32000, + "output": 16384 + } + }, + "llama-3.3-70b-ignition-v0.1": { + "id": "Llama-3.3-70B-Ignition-v0.1", + "reasoning": false, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 32768, + "input": 32768, + "output": 16384 + } + }, + "glm-z1-air": { + "id": "glm-z1-air", + "reasoning": false, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 32000, + "input": 32000, + "output": 16384 + } + }, + "llama-3.3-70b-rawmaw": { + "id": "Llama-3.3-70B-RAWMAW", + "reasoning": false, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 32768, + "input": 32768, + "output": 16384 + } + }, + "magistral-small-2506": { + "id": "Magistral-Small-2506", + "reasoning": false, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 32768, + "input": 32768, + "output": 32768 + } + }, + "ernie-x1-turbo-32k": { + "id": "ernie-x1-turbo-32k", + "reasoning": false, + "toolCall": false, + "modalities": { + "input": [ + "text", + "image", + "pdf" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 32000, + "input": 32000, + "output": 16384 + } + }, + "deepseek-r1-sambanova": { + "id": "deepseek-r1-sambanova", + "reasoning": false, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "input": 128000, + "output": 4096 + } + }, + "claude-3-7-sonnet-thinking:1024": { + "id": "claude-3-7-sonnet-thinking:1024", + "reasoning": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image", + "pdf" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 200000, + "input": 200000, + "output": 64000 + } + }, + "llama-3.3-70b-magnum-v4-se-cirrus-x1-slerp": { + "id": "Llama-3.3-70B-Magnum-v4-SE-Cirrus-x1-SLERP", + "reasoning": false, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 32768, + "input": 32768, + "output": 16384 + } + }, + "llama-3.3-70b-arliai-rpmax-v3": { + "id": "Llama-3.3-70B-ArliAI-RPMax-v3", + "reasoning": false, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 32768, + "input": 32768, + "output": 16384 + } + }, + "qwen-long": { + "id": "qwen-long", + "reasoning": false, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 10000000, + "input": 10000000, + "output": 8192 + }, + "family": "qwen", + "temperature": true + }, + "llama-3.3-70b-progenitor-v3.3": { + "id": "Llama-3.3-70B-Progenitor-V3.3", + "reasoning": false, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 32768, + "input": 32768, + "output": 16384 + } + }, + "glm-4.5-air-derestricted-iceblink-v2": { + "id": "GLM-4.5-Air-Derestricted-Iceblink-v2", + "reasoning": false, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 158600, + "input": 158600, + "output": 65536 + } + }, + "study_gpt-chatgpt-4o-latest": { + "id": "study_gpt-chatgpt-4o-latest", + "reasoning": false, + "toolCall": false, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 200000, + "input": 200000, + "output": 16384 + } + }, + "qwq-32b": { + "id": "qwq-32b", + "reasoning": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 131072, + "input": 128000, + "output": 8192 + }, + "family": "qwen", + "temperature": true + }, + "llama-3.3-70b-ms-nevoria": { + "id": "Llama-3.3-70B-MS-Nevoria", + "reasoning": false, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 32768, + "input": 32768, + "output": 16384 + } + }, + "doubao-seed-1-6-250615": { + "id": "doubao-seed-1-6-250615", + "reasoning": false, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 256000, + "input": 256000, + "output": 16384 + } + }, + "glm-4": { + "id": "glm-4", + "reasoning": false, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "input": 128000, + "output": 4096 + } + }, + "azure-gpt-4-turbo": { + "id": "azure-gpt-4-turbo", + "reasoning": false, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "input": 128000, + "output": 4096 + } + }, + "llama-3.3-70b-legion-v2.1": { + "id": "Llama-3.3-70B-Legion-V2.1", + "reasoning": false, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 32768, + "input": 32768, + "output": 16384 + } + }, + "claude-3-7-sonnet-thinking:32768": { + "id": "claude-3-7-sonnet-thinking:32768", + "reasoning": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image", + "pdf" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 200000, + "input": 200000, + "output": 64000 + } + }, + "asi1-mini": { + "id": "asi1-mini", + "reasoning": false, + "toolCall": false, + "modalities": { + "input": [ + "text", + "pdf" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "input": 128000, + "output": 16384 + } + }, + "gemini-exp-1206": { + "id": "gemini-exp-1206", + "reasoning": false, + "toolCall": false, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 2097152, + "input": 2097152, + "output": 8192 + } + }, + "brave": { + "id": "brave", + "reasoning": false, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 8192, + "input": 8192, + "output": 8192 + } + }, + "doubao-1-5-thinking-pro-250415": { + "id": "doubao-1-5-thinking-pro-250415", + "reasoning": false, + "toolCall": false, + "modalities": { + "input": [ + "text", + "pdf" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "input": 128000, + "output": 16384 + } + }, + "claude-sonnet-4-thinking:64000": { + "id": "claude-sonnet-4-thinking:64000", + "reasoning": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image", + "pdf" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 1000000, + "input": 1000000, + "output": 64000 + } + }, + "glm-4.5-air-derestricted-steam-reextract": { + "id": "GLM-4.5-Air-Derestricted-Steam-ReExtract", + "reasoning": false, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 131072, + "input": 131072, + "output": 65536 + } + }, + "kimi-k2-instruct-fast": { + "id": "kimi-k2-instruct-fast", + "reasoning": false, + "toolCall": false, + "modalities": { + "input": [ + "text", + "pdf" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 131072, + "input": 131072, + "output": 16384 + } + }, + "llama-3.3-70b-geneticlemonade-opus": { + "id": "Llama-3.3-70B-GeneticLemonade-Opus", + "reasoning": false, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 32768, + "input": 32768, + "output": 16384 + } + }, + "gemma-3-27b-big-tiger-v3": { + "id": "Gemma-3-27B-Big-Tiger-v3", + "reasoning": false, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 32768, + "input": 32768, + "output": 16384 + } + }, + "doubao-seed-2-0-mini-260215": { + "id": "doubao-seed-2-0-mini-260215", + "reasoning": false, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 256000, + "input": 256000, + "output": 32000 + } + }, + "glm-4-air": { + "id": "glm-4-air", + "reasoning": false, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "input": 128000, + "output": 4096 + } + }, + "glm-4.5-air-derestricted-iceblink-reextract": { + "id": "GLM-4.5-Air-Derestricted-Iceblink-ReExtract", + "reasoning": false, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 131072, + "input": 131072, + "output": 98304 + } + }, + "gemini-2.0-pro-reasoner": { + "id": "gemini-2.0-pro-reasoner", + "reasoning": false, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "input": 128000, + "output": 65536 + } + }, + "gemini-2.0-flash-001": { + "id": "gemini-2.0-flash-001", + "reasoning": false, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 1000000, + "input": 1000000, + "output": 8192 + } + }, + "glm-4-plus": { + "id": "glm-4-plus", + "reasoning": false, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "input": 128000, + "output": 4096 + } + }, + "gemini-2.0-flash-exp-image-generation": { + "id": "gemini-2.0-flash-exp-image-generation", + "reasoning": false, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 32767, + "input": 32767, + "output": 8192 + } + }, + "glm-4.5-air-derestricted": { + "id": "GLM-4.5-Air-Derestricted", + "reasoning": false, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 202600, + "input": 202600, + "output": 98304 + } + }, + "gemini-2.0-flash-thinking-exp-1219": { + "id": "gemini-2.0-flash-thinking-exp-1219", + "reasoning": false, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 32767, + "input": 32767, + "output": 8192 + } + }, + "glm-4.1v-thinking-flashx": { + "id": "glm-4.1v-thinking-flashx", + "reasoning": false, + "toolCall": false, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 64000, + "input": 64000, + "output": 8192 + } + }, + "llama-3.3-70b-strawberrylemonade-v1.0": { + "id": "Llama-3.3-70B-StrawberryLemonade-v1.0", + "reasoning": false, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 32768, + "input": 32768, + "output": 16384 + } + }, + "llama-3.3-70b-fallen-v1": { + "id": "Llama-3.3-70B-Fallen-v1", + "reasoning": false, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 32768, + "input": 32768, + "output": 16384 + } + }, + "gemma-3-27b-nidum-uncensored": { + "id": "Gemma-3-27B-Nidum-Uncensored", + "reasoning": false, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 32768, + "input": 32768, + "output": 96000 + } + }, + "llama-3.3-70b-electranova-v1.0": { + "id": "Llama-3.3-70B-Electranova-v1.0", + "reasoning": false, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 32768, + "input": 32768, + "output": 16384 + } + }, + "grok-3-fast-beta": { + "id": "grok-3-fast-beta", + "reasoning": false, + "toolCall": false, + "modalities": { + "input": [ + "text", + "pdf" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 131072, + "input": 131072, + "output": 131072 + } + }, + "llama-3.3-70b-sapphira-0.1": { + "id": "Llama-3.3-70B-Sapphira-0.1", + "reasoning": false, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 32768, + "input": 32768, + "output": 16384 + } + }, + "gemini-2.5-pro-preview-03-25": { + "id": "gemini-2.5-pro-preview-03-25", + "reasoning": true, + "toolCall": false, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 1048756, + "input": 1048756, + "output": 65536 + } + }, + "step-2-16k-exp": { + "id": "step-2-16k-exp", + "reasoning": false, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 16000, + "input": 16000, + "output": 8192 + } + }, + "chroma": { + "id": "chroma", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "image" + ] + }, + "limit": { + "context": 0, + "output": 0 + } + }, + "fastgpt": { + "id": "fastgpt", + "reasoning": false, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 32768, + "input": 32768, + "output": 32768 + } + }, + "claude-sonnet-4-thinking:8192": { + "id": "claude-sonnet-4-thinking:8192", + "reasoning": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image", + "pdf" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 1000000, + "input": 1000000, + "output": 64000 + } + }, + "llama-3.3-70b-electra-r1": { + "id": "Llama-3.3-70B-Electra-R1", + "reasoning": false, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 32768, + "input": 32768, + "output": 16384 + } + }, + "llama-3.3-70b-fallen-r1-v1": { + "id": "Llama-3.3-70B-Fallen-R1-v1", + "reasoning": false, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 32768, + "input": 32768, + "output": 16384 + } + }, + "gemma-3-27b-it-abliterated": { + "id": "Gemma-3-27B-it-Abliterated", + "reasoning": false, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 32768, + "input": 32768, + "output": 96000 + } + }, + "doubao-1.5-pro-256k": { + "id": "doubao-1.5-pro-256k", + "reasoning": false, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 256000, + "input": 256000, + "output": 16384 + } + }, + "claude-opus-4-thinking": { + "id": "claude-opus-4-thinking", + "reasoning": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image", + "pdf" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 200000, + "input": 200000, + "output": 32000 + } + }, + "doubao-1-5-thinking-vision-pro-250428": { + "id": "doubao-1-5-thinking-vision-pro-250428", + "reasoning": false, + "toolCall": false, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "input": 128000, + "output": 16384 + } + }, + "doubao-seed-2-0-lite-260215": { + "id": "doubao-seed-2-0-lite-260215", + "reasoning": false, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 256000, + "input": 256000, + "output": 32000 + } + }, + "qwen25-vl-72b-instruct": { + "id": "qwen25-vl-72b-instruct", + "reasoning": false, + "toolCall": false, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 32000, + "input": 32000, + "output": 32768 + } + }, + "azure-gpt-4o": { + "id": "azure-gpt-4o", + "reasoning": false, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "input": 128000, + "output": 16384 + } + }, + "ernie-4.5-turbo-128k": { + "id": "ernie-4.5-turbo-128k", + "reasoning": false, + "toolCall": false, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "input": 128000, + "output": 16384 + } + }, + "azure-o1": { + "id": "azure-o1", + "reasoning": false, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 200000, + "input": 200000, + "output": 100000 + } + }, + "gemini-3-pro-preview-thinking": { + "id": "gemini-3-pro-preview-thinking", + "reasoning": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 1048756, + "input": 1048756, + "output": 65536 + } + }, + "grok-3-mini-beta": { + "id": "grok-3-mini-beta", + "reasoning": false, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 131072, + "input": 131072, + "output": 131072 + } + }, + "claude-opus-4-1-thinking": { + "id": "claude-opus-4-1-thinking", + "reasoning": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image", + "pdf" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 200000, + "input": 200000, + "output": 32000 + } + }, + "gemini-2.5-flash-nothinking": { + "id": "gemini-2.5-flash-nothinking", + "reasoning": false, + "toolCall": false, + "modalities": { + "input": [ + "text", + "image", + "pdf" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 1048756, + "input": 1048756, + "output": 65536 + } + }, + "claude-3-7-sonnet-thinking:8192": { + "id": "claude-3-7-sonnet-thinking:8192", + "reasoning": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image", + "pdf" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 200000, + "input": 200000, + "output": 64000 + } + }, + "auto-model-basic": { + "id": "auto-model-basic", + "reasoning": false, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 1000000, + "input": 1000000, + "output": 1000000 + } + }, + "llama-3.3-70b-the-omega-directive-unslop-v2.1": { + "id": "Llama-3.3-70B-The-Omega-Directive-Unslop-v2.1", + "reasoning": false, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 32768, + "input": 32768, + "output": 16384 + } + }, + "glm-4-plus-0111": { + "id": "glm-4-plus-0111", + "reasoning": false, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "input": 128000, + "output": 4096 + } + }, + "llama-3.3-70b-bigger-body": { + "id": "Llama-3.3-70B-Bigger-Body", + "reasoning": false, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 32768, + "input": 32768, + "output": 16384 + } + }, + "kat-coder-air-v1": { + "id": "KAT-Coder-Air-V1", + "reasoning": false, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "input": 128000, + "output": 32768 + } + }, + "doubao-seed-1-6-flash-250615": { + "id": "doubao-seed-1-6-flash-250615", + "reasoning": false, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 256000, + "input": 256000, + "output": 16384 + } + }, + "glm-4-air-0111": { + "id": "glm-4-air-0111", + "reasoning": false, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "input": 128000, + "output": 4096 + } + }, + "phi-4-mini-instruct": { + "id": "phi-4-mini-instruct", + "reasoning": false, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "input": 128000, + "output": 16384 + } + }, + "jamba-mini-1.6": { + "id": "jamba-mini-1.6", + "reasoning": false, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 256000, + "input": 256000, + "output": 4096 + } + }, + "kimi-thinking-preview": { + "id": "kimi-thinking-preview", + "reasoning": false, + "toolCall": false, + "modalities": { + "input": [ + "text", + "pdf" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "input": 128000, + "output": 16384 + } + }, + "claude-sonnet-4-thinking:1024": { + "id": "claude-sonnet-4-thinking:1024", + "reasoning": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image", + "pdf" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 1000000, + "input": 1000000, + "output": 64000 + } + }, + "llama-3.3-70b-incandescent-malevolence": { + "id": "Llama-3.3-70B-Incandescent-Malevolence", + "reasoning": false, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 32768, + "input": 32768, + "output": 16384 + } + }, + "llama-3.3-70b-forgotten-safeword-3.6": { + "id": "Llama-3.3-70B-Forgotten-Safeword-3.6", + "reasoning": false, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 32768, + "input": 32768, + "output": 16384 + } + }, + "step-2-mini": { + "id": "step-2-mini", + "reasoning": false, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 8000, + "input": 8000, + "output": 4096 + } + }, + "mistral-nemo-12b-instruct-2407": { + "id": "Mistral-Nemo-12B-Instruct-2407", + "reasoning": false, + "toolCall": false, + "modalities": { + "input": [ + "text", + "pdf" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 16384, + "input": 16384, + "output": 16384 + } + }, + "baichuan4-turbo": { + "id": "Baichuan4-Turbo", + "reasoning": false, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "input": 128000, + "output": 32768 + } + }, + "ernie-5.0-thinking-latest": { + "id": "ernie-5.0-thinking-latest", + "reasoning": true, + "toolCall": false, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "input": 128000, + "output": 16384 + } + }, + "gemma-3-27b-glitter": { + "id": "Gemma-3-27B-Glitter", + "reasoning": false, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 32768, + "input": 32768, + "output": 16384 + } + }, + "claude-opus-4-thinking:32000": { + "id": "claude-opus-4-thinking:32000", + "reasoning": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image", + "pdf" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 200000, + "input": 200000, + "output": 32000 + } + }, + "auto-model-premium": { + "id": "auto-model-premium", + "reasoning": false, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 1000000, + "input": 1000000, + "output": 1000000 + } + }, + "gemini-2.0-flash-thinking-exp-01-21": { + "id": "gemini-2.0-flash-thinking-exp-01-21", + "reasoning": true, + "toolCall": false, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 1000000, + "input": 1000000, + "output": 8192 + } + }, + "claude-sonnet-4-thinking:32768": { + "id": "claude-sonnet-4-thinking:32768", + "reasoning": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image", + "pdf" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 1000000, + "input": 1000000, + "output": 64000 + } + }, + "claude-opus-4-1-thinking:32768": { + "id": "claude-opus-4-1-thinking:32768", + "reasoning": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image", + "pdf" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 200000, + "input": 200000, + "output": 32000 + } + }, + "jamba-large": { + "id": "jamba-large", + "reasoning": false, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 256000, + "input": 256000, + "output": 4096 + } + }, + "llama-3.3-70b-miraifanfare": { + "id": "Llama-3.3-70B-MiraiFanfare", + "reasoning": false, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 32768, + "input": 32768, + "output": 16384 + } + }, + "venice-uncensored:web": { + "id": "venice-uncensored:web", + "reasoning": false, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 80000, + "input": 80000, + "output": 16384 + } + }, + "gemini-2.5-flash-lite-preview-09-2025-thinking": { + "id": "gemini-2.5-flash-lite-preview-09-2025-thinking", + "reasoning": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image", + "pdf" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 1048756, + "input": 1048756, + "output": 65536 + } + }, + "ernie-x1-32k-preview": { + "id": "ernie-x1-32k-preview", + "reasoning": false, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 32000, + "input": 32000, + "output": 16384 + } + }, + "glm-z1-airx": { + "id": "glm-z1-airx", + "reasoning": false, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 32000, + "input": 32000, + "output": 16384 + } + }, + "ernie-x1.1-preview": { + "id": "ernie-x1.1-preview", + "reasoning": false, + "toolCall": false, + "modalities": { + "input": [ + "text", + "pdf" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 64000, + "input": 64000, + "output": 8192 + } + }, + "exa-research": { + "id": "exa-research", + "reasoning": false, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 8192, + "input": 8192, + "output": 8192 + } + }, + "llama-3.3-70b-mokume-gane-r1": { + "id": "Llama-3.3-70B-Mokume-Gane-R1", + "reasoning": false, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 32768, + "input": 32768, + "output": 16384 + } + }, + "glm-4.1v-thinking-flash": { + "id": "glm-4.1v-thinking-flash", + "reasoning": false, + "toolCall": false, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 64000, + "input": 64000, + "output": 8192 + } + }, + "llama-3.3-70b-geneticlemonade-unleashed-v3": { + "id": "Llama-3.3-70B-GeneticLemonade-Unleashed-v3", + "reasoning": false, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 32768, + "input": 32768, + "output": 16384 + } + }, + "llama-3.3-70b-predatorial-extasy": { + "id": "Llama-3.3-70B-Predatorial-Extasy", + "reasoning": false, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 32768, + "input": 32768, + "output": 16384 + } + }, + "glm-4-airx": { + "id": "glm-4-airx", + "reasoning": false, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 8000, + "input": 8000, + "output": 4096 + } + }, + "doubao-seed-1-6-thinking-250615": { + "id": "doubao-seed-1-6-thinking-250615", + "reasoning": false, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 256000, + "input": 256000, + "output": 16384 + } + }, + "claude-3-7-sonnet-thinking": { + "id": "claude-3-7-sonnet-thinking", + "reasoning": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image", + "pdf" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 200000, + "input": 200000, + "output": 16000 + } + }, + "glm-4.5-air-derestricted-steam": { + "id": "GLM-4.5-Air-Derestricted-Steam", + "reasoning": false, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 220600, + "input": 220600, + "output": 65536 + } + }, + "ernie-5.0-thinking-preview": { + "id": "ernie-5.0-thinking-preview", + "reasoning": true, + "toolCall": false, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "input": 128000, + "output": 16384 + } + }, + "claude-opus-4-thinking:1024": { + "id": "claude-opus-4-thinking:1024", + "reasoning": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image", + "pdf" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 200000, + "input": 200000, + "output": 32000 + } + }, + "llama-3.3-70b-strawberrylemonade-v1.2": { + "id": "Llama-3.3-70B-Strawberrylemonade-v1.2", + "reasoning": false, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 32768, + "input": 32768, + "output": 16384 + } + }, + "llama-3.3-70b-vulpecula-r1": { + "id": "Llama-3.3-70B-Vulpecula-R1", + "reasoning": false, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 32768, + "input": 32768, + "output": 16384 + } + }, + "glm-4.6-derestricted-v5": { + "id": "GLM-4.6-Derestricted-v5", + "reasoning": false, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 131072, + "input": 131072, + "output": 8192 + } + }, + "llama-3.3-70b-cirrus-x1": { + "id": "Llama-3.3-70B-Cirrus-x1", + "reasoning": false, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 32768, + "input": 32768, + "output": 16384 + } + }, + "llama-3.3-70b-arliai-rpmax-v2": { + "id": "Llama-3.3-70B-ArliAI-RPMax-v2", + "reasoning": false, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 32768, + "input": 32768, + "output": 16384 + } + }, + "doubao-seed-code-preview-latest": { + "id": "doubao-seed-code-preview-latest", + "reasoning": true, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 256000, + "input": 256000, + "output": 16384 + } + }, + "llama-3.3+(3.1v3.3)-70b-new-dawn-v1.1": { + "id": "Llama-3.3+(3.1v3.3)-70B-New-Dawn-v1.1", + "reasoning": false, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 32768, + "input": 32768, + "output": 16384 + } + }, + "qwen3-vl-235b-a22b-thinking": { + "id": "qwen3-vl-235b-a22b-thinking", + "reasoning": true, + "toolCall": false, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 32768, + "input": 32768, + "output": 32768 + } + }, + "claude-sonnet-4-thinking": { + "id": "claude-sonnet-4-thinking", + "reasoning": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image", + "pdf" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 1000000, + "input": 1000000, + "output": 64000 + } + }, + "qwen2.5-32b-eva-v0.2": { + "id": "Qwen2.5-32B-EVA-v0.2", + "reasoning": false, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 24576, + "input": 24576, + "output": 8192 + } + }, + "llama-3.3-70b-cu-mai-r1": { + "id": "Llama-3.3-70B-Cu-Mai-R1", + "reasoning": false, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 32768, + "input": 32768, + "output": 16384 + } + }, + "hidream": { + "id": "hidream", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "image" + ] + }, + "limit": { + "context": 0, + "output": 0 + } + }, + "auto-model": { + "id": "auto-model", + "reasoning": false, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 1000000, + "input": 1000000, + "output": 1000000 + } + }, + "jamba-mini-1.7": { + "id": "jamba-mini-1.7", + "reasoning": false, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 256000, + "input": 256000, + "output": 4096 + } + }, + "doubao-seed-2-0-pro-260215": { + "id": "doubao-seed-2-0-pro-260215", + "reasoning": false, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 256000, + "input": 256000, + "output": 128000 + } + }, + "llama-3.3-70b-nova": { + "id": "Llama-3.3-70B-Nova", + "reasoning": false, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 32768, + "input": 32768, + "output": 16384 + } + }, + "gemini-2.5-flash-preview-09-2025-thinking": { + "id": "gemini-2.5-flash-preview-09-2025-thinking", + "reasoning": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image", + "pdf" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 1048756, + "input": 1048756, + "output": 65536 + } + }, + "llama-3.3-70b-sapphira-0.2": { + "id": "Llama-3.3-70B-Sapphira-0.2", + "reasoning": false, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 32768, + "input": 32768, + "output": 16384 + } + }, + "auto-model-standard": { + "id": "auto-model-standard", + "reasoning": false, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 1000000, + "input": 1000000, + "output": 1000000 + } + }, + "grok-3-mini-fast-beta": { + "id": "grok-3-mini-fast-beta", + "reasoning": false, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 131072, + "input": 131072, + "output": 131072 + } + }, + "meta-llama-3-1-8b-instruct-fp8": { + "id": "Meta-Llama-3-1-8B-Instruct-FP8", + "reasoning": false, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "input": 128000, + "output": 16384 + } + }, + "step-3": { + "id": "step-3", + "reasoning": false, + "toolCall": false, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 65536, + "input": 65536, + "output": 8192 + } + }, + "universal-summarizer": { + "id": "universal-summarizer", + "reasoning": false, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 32768, + "input": 32768, + "output": 32768 + } + }, + "deepclaude": { + "id": "deepclaude", + "reasoning": false, + "toolCall": false, + "modalities": { + "input": [ + "text", + "pdf" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "input": 128000, + "output": 8192 + } + }, + "brave-pro": { + "id": "brave-pro", + "reasoning": false, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 8192, + "input": 8192, + "output": 8192 + } + }, + "claude-3-7-sonnet-reasoner": { + "id": "claude-3-7-sonnet-reasoner", + "reasoning": false, + "toolCall": false, + "modalities": { + "input": [ + "text", + "pdf" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "input": 128000, + "output": 8192 + } + }, + "claude-opus-4-thinking:8192": { + "id": "claude-opus-4-thinking:8192", + "reasoning": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image", + "pdf" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 200000, + "input": 200000, + "output": 32000 + } + }, + "claude-opus-4-thinking:32768": { + "id": "claude-opus-4-thinking:32768", + "reasoning": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image", + "pdf" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 200000, + "input": 200000, + "output": 32000 + } + }, + "glm-zero-preview": { + "id": "glm-zero-preview", + "reasoning": false, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 8000, + "input": 8000, + "output": 4096 + } + }, + "azure-gpt-4o-mini": { + "id": "azure-gpt-4o-mini", + "reasoning": false, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "input": 128000, + "output": 16384 + } + }, + "deepseek-math-v2": { + "id": "deepseek-math-v2", + "reasoning": false, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "input": 128000, + "output": 65536 + } + }, + "glm-4-long": { + "id": "glm-4-long", + "reasoning": false, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 1000000, + "input": 1000000, + "output": 4096 + } + }, + "glm-4.5-air-derestricted-iceblink": { + "id": "GLM-4.5-Air-Derestricted-Iceblink", + "reasoning": false, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 131072, + "input": 131072, + "output": 98304 + } + }, + "claude-opus-4-1-thinking:1024": { + "id": "claude-opus-4-1-thinking:1024", + "reasoning": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image", + "pdf" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 200000, + "input": 200000, + "output": 32000 + } + }, + "qwen3-vl-235b-a22b-instruct-original": { + "id": "qwen3-vl-235b-a22b-instruct-original", + "reasoning": false, + "toolCall": false, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 32768, + "input": 32768, + "output": 32768 + } + }, + "llama-3.3+(3.1v3.3)-70b-hanami-x1": { + "id": "Llama-3.3+(3.1v3.3)-70B-Hanami-x1", + "reasoning": false, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 32768, + "input": 32768, + "output": 16384 + } + }, + "claude-opus-4-1-thinking:8192": { + "id": "claude-opus-4-1-thinking:8192", + "reasoning": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image", + "pdf" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 200000, + "input": 200000, + "output": 32000 + } + }, + "llama-3.3-70b-damascus-r1": { + "id": "Llama-3.3-70B-Damascus-R1", + "reasoning": false, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 32768, + "input": 32768, + "output": 16384 + } + }, + "gemma-3-27b-arliai-rpmax-v3": { + "id": "Gemma-3-27B-ArliAI-RPMax-v3", + "reasoning": false, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 32768, + "input": 32768, + "output": 16384 + } + }, + "gemini-2.5-flash-preview-05-20:thinking": { + "id": "gemini-2.5-flash-preview-05-20:thinking", + "reasoning": true, + "toolCall": false, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 1048000, + "input": 1048000, + "output": 65536 + } + }, + "claude-opus-4-1-thinking:32000": { + "id": "claude-opus-4-1-thinking:32000", + "reasoning": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image", + "pdf" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 200000, + "input": 200000, + "output": 32000 + } + }, + "sarvan-medium": { + "id": "sarvan-medium", + "reasoning": false, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "input": 128000, + "output": 16384 + } + }, + "llama-3.3-70b-anthrobomination": { + "id": "Llama-3.3-70B-Anthrobomination", + "reasoning": false, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 32768, + "input": 32768, + "output": 16384 + } + }, + "baichuan4-air": { + "id": "Baichuan4-Air", + "reasoning": false, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 32768, + "input": 32768, + "output": 32768 + } + }, + "jamba-mini": { + "id": "jamba-mini", + "reasoning": false, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 256000, + "input": 256000, + "output": 4096 + } + }, + "kat-coder-exp-72b-1010": { + "id": "KAT-Coder-Exp-72B-1010", + "reasoning": false, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "input": 128000, + "output": 32768 + } + }, + "gemini-2.5-flash-preview-04-17:thinking": { + "id": "gemini-2.5-flash-preview-04-17:thinking", + "reasoning": true, + "toolCall": false, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 1048756, + "input": 1048756, + "output": 65536 + } + }, + "brave-research": { + "id": "brave-research", + "reasoning": false, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 16384, + "input": 16384, + "output": 16384 + } + }, + "llama-3.3-70b-argunaut-1-sft": { + "id": "Llama-3.3-70B-Argunaut-1-SFT", + "reasoning": false, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 32768, + "input": 32768, + "output": 16384 + } + }, + "claude-opus-4-5-20251101:thinking": { + "id": "claude-opus-4-5-20251101:thinking", + "reasoning": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image", + "pdf" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 200000, + "input": 200000, + "output": 32000 + } + }, + "grok-3-beta": { + "id": "grok-3-beta", + "reasoning": false, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 131072, + "input": 131072, + "output": 131072 + } + }, + "azure-o3-mini": { + "id": "azure-o3-mini", + "reasoning": false, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 200000, + "input": 200000, + "output": 65536 + } + }, + "qwq-32b-arliai-rpr-v1": { + "id": "QwQ-32B-ArliAI-RpR-v1", + "reasoning": false, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 32768, + "input": 32768, + "output": 32768 + } + }, + "llama-3.3-70b-forgotten-abomination-v5.0": { + "id": "Llama-3.3-70B-Forgotten-Abomination-v5.0", + "reasoning": false, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 32768, + "input": 32768, + "output": 16384 + } + }, + "doubao-seed-2-0-code-preview-260215": { + "id": "doubao-seed-2-0-code-preview-260215", + "reasoning": false, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 256000, + "input": 256000, + "output": 128000 + } + }, + "llama-3.3-70b-mhnnn-x1": { + "id": "Llama-3.3-70B-Mhnnn-x1", + "reasoning": false, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 32768, + "input": 32768, + "output": 16384 + } + }, + "hunyuan-t1-latest": { + "id": "hunyuan-t1-latest", + "reasoning": false, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 256000, + "input": 256000, + "output": 16384 + } + }, + "gemma-3-27b-cardprojector-v4": { + "id": "Gemma-3-27B-CardProjector-v4", + "reasoning": false, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 32768, + "input": 32768, + "output": 16384 + } + }, + "glm-4-flash": { + "id": "glm-4-flash", + "reasoning": false, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "input": 128000, + "output": 4096 + } + }, + "learnlm-1.5-pro-experimental": { + "id": "learnlm-1.5-pro-experimental", + "reasoning": false, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 32767, + "input": 32767, + "output": 8192 + } + }, + "llama-3.3-70b-dark-ages-v0.1": { + "id": "Llama-3.3-70B-Dark-Ages-v0.1", + "reasoning": false, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 32768, + "input": 32768, + "output": 16384 + } + }, + "yi-large": { + "id": "yi-large", + "reasoning": false, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 32000, + "input": 32000, + "output": 4096 + } + }, + "exa-answer": { + "id": "exa-answer", + "reasoning": false, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 4096, + "input": 4096, + "output": 4096 + } + }, + "gemini-2.5-pro-exp-03-25": { + "id": "gemini-2.5-pro-exp-03-25", + "reasoning": true, + "toolCall": false, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 1048756, + "input": 1048756, + "output": 65536 + } + }, + "llm360/k2-think": { + "id": "LLM360/K2-Think", + "family": "kimi-thinking", + "reasoning": false, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "input": 128000, + "output": 32768 + } + }, + "abacusai/dracarys-72b-instruct": { + "id": "abacusai/Dracarys-72B-Instruct", + "family": "llama", + "reasoning": false, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 16384, + "input": 16384, + "output": 8192 + } + }, + "envoid/llama-3.05-nemotron-tenyxchat-storybreaker-70b": { + "id": "Envoid/Llama-3.05-Nemotron-Tenyxchat-Storybreaker-70B", + "family": "nemotron", + "reasoning": false, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 16384, + "input": 16384, + "output": 8192 + } + }, + "envoid/llama-3.05-nt-storybreaker-ministral-70b": { + "id": "Envoid/Llama-3.05-NT-Storybreaker-Ministral-70B", + "family": "llama", + "reasoning": false, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 16384, + "input": 16384, + "output": 8192 + } + }, + "zai-org/glm-5:thinking": { + "id": "zai-org/glm-5:thinking", + "family": "glm", + "reasoning": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 200000, + "input": 200000, + "output": 128000 + } + }, + "nvidia/llama-3.1-nemotron-70b-instruct-hf": { + "id": "nvidia/Llama-3.1-Nemotron-70B-Instruct-HF", + "family": "nemotron", + "reasoning": false, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 16384, + "input": 16384, + "output": 8192 + } + }, + "nvidia/llama-3_3-nemotron-super-49b-v1_5": { + "id": "nvidia/Llama-3_3-Nemotron-Super-49B-v1_5", + "family": "nemotron", + "reasoning": false, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "input": 128000, + "output": 16384 + } + }, + "doctor-shotgun/ms3.2-24b-magnum-diamond": { + "id": "Doctor-Shotgun/MS3.2-24B-Magnum-Diamond", + "family": "mistral", + "reasoning": false, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 16384, + "input": 16384, + "output": 32768 + } + }, + "arcee-ai/trinity-large": { + "id": "arcee-ai/trinity-large", + "family": "trinity", + "reasoning": false, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 131072, + "input": 131072, + "output": 8192 + } + }, + "meganova-ai/manta-flash-1.0": { + "id": "meganova-ai/manta-flash-1.0", + "family": "nova", + "reasoning": false, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 16384, + "input": 16384, + "output": 16384 + } + }, + "meganova-ai/manta-pro-1.0": { + "id": "meganova-ai/manta-pro-1.0", + "family": "nova", + "reasoning": false, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 32768, + "input": 32768, + "output": 32768 + } + }, + "meganova-ai/manta-mini-1.0": { + "id": "meganova-ai/manta-mini-1.0", + "family": "nova", + "reasoning": false, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 8192, + "input": 8192, + "output": 8192 + } + }, + "xiaomi/mimo-v2-flash-original": { + "id": "xiaomi/mimo-v2-flash-original", + "family": "mimo", + "reasoning": false, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 256000, + "input": 256000, + "output": 32768 + } + }, + "xiaomi/mimo-v2-flash-thinking": { + "id": "xiaomi/mimo-v2-flash-thinking", + "family": "mimo", + "reasoning": false, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 256000, + "input": 256000, + "output": 32768 + } + }, + "xiaomi/mimo-v2-flash-thinking-original": { + "id": "xiaomi/mimo-v2-flash-thinking-original", + "family": "mimo", + "reasoning": false, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 256000, + "input": 256000, + "output": 32768 + } + }, + "microsoft/mai-ds-r1-fp8": { + "id": "microsoft/MAI-DS-R1-FP8", + "family": "deepseek", + "reasoning": false, + "toolCall": false, + "modalities": { + "input": [ + "text", + "pdf" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "input": 128000, + "output": 8192 + } + }, + "failspy/meta-llama-3-70b-instruct-abliterated-v3.5": { + "id": "failspy/Meta-Llama-3-70B-Instruct-abliterated-v3.5", + "family": "llama", + "reasoning": false, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 8192, + "input": 8192, + "output": 8192 + } + }, + "featherless-ai/qwerky-72b": { + "id": "featherless-ai/Qwerky-72B", + "family": "qwerky", + "reasoning": false, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 32000, + "input": 32000, + "output": 8192 + } + }, + "tee/glm-5": { + "id": "TEE/glm-5", + "family": "glm", + "reasoning": false, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 203000, + "input": 203000, + "output": 65535 + } + }, + "tee/deepseek-v3.1": { + "id": "TEE/deepseek-v3.1", + "family": "deepseek", + "reasoning": false, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 164000, + "input": 164000, + "output": 8192 + } + }, + "tee/glm-4.7-flash": { + "id": "TEE/glm-4.7-flash", + "family": "glm-flash", + "reasoning": false, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 203000, + "input": 203000, + "output": 65535 + } + }, + "tee/qwen3-coder": { + "id": "TEE/qwen3-coder", + "family": "qwen", + "reasoning": false, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "input": 128000, + "output": 32768 + } + }, + "tee/glm-4.6": { + "id": "TEE/glm-4.6", + "family": "glm", + "reasoning": false, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 203000, + "input": 203000, + "output": 65535 + } + }, + "tee/deepseek-r1-0528": { + "id": "TEE/deepseek-r1-0528", + "family": "deepseek", + "reasoning": false, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "input": 128000, + "output": 65536 + } + }, + "tee/minimax-m2.1": { + "id": "TEE/minimax-m2.1", + "family": "minimax", + "reasoning": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 200000, + "input": 200000, + "output": 131072 + } + }, + "tee/qwen3.5-397b-a17b": { + "id": "TEE/qwen3.5-397b-a17b", + "family": "qwen", + "reasoning": false, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 258048, + "input": 258048, + "output": 65536 + } + }, + "tee/gpt-oss-120b": { + "id": "TEE/gpt-oss-120b", + "family": "gpt-oss", + "reasoning": false, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 131072, + "input": 131072, + "output": 16384 + } + }, + "tee/kimi-k2.5": { + "id": "TEE/kimi-k2.5", + "family": "kimi", + "reasoning": false, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "input": 128000, + "output": 65535 + } + }, + "tee/qwen3-30b-a3b-instruct-2507": { + "id": "TEE/qwen3-30b-a3b-instruct-2507", + "family": "qwen", + "reasoning": false, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 262000, + "input": 262000, + "output": 32768 + } + }, + "tee/kimi-k2.5-thinking": { + "id": "TEE/kimi-k2.5-thinking", + "family": "kimi-thinking", + "reasoning": true, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "input": 128000, + "output": 65535 + } + }, + "tee/qwen2.5-vl-72b-instruct": { + "id": "TEE/qwen2.5-vl-72b-instruct", + "family": "qwen", + "reasoning": false, + "toolCall": false, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 65536, + "input": 65536, + "output": 8192 + } + }, + "tee/deepseek-v3.2": { + "id": "TEE/deepseek-v3.2", + "family": "deepseek", + "reasoning": false, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 164000, + "input": 164000, + "output": 65536 + } + }, + "tee/glm-4.7": { + "id": "TEE/glm-4.7", + "family": "glm", + "reasoning": false, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 131000, + "input": 131000, + "output": 65535 + } + }, + "tee/kimi-k2-thinking": { + "id": "TEE/kimi-k2-thinking", + "family": "kimi-thinking", + "reasoning": false, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "input": 128000, + "output": 65535 + } + }, + "tee/llama3-3-70b": { + "id": "TEE/llama3-3-70b", + "family": "llama", + "reasoning": false, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "input": 128000, + "output": 16384 + } + }, + "tee/gemma-3-27b-it": { + "id": "TEE/gemma-3-27b-it", + "family": "gemma", + "reasoning": false, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 131072, + "input": 131072, + "output": 8192 + } + }, + "tee/gpt-oss-20b": { + "id": "TEE/gpt-oss-20b", + "family": "gpt-oss", + "reasoning": false, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 131072, + "input": 131072, + "output": 8192 + } + }, + "anthracite-org/magnum-v2-72b": { + "id": "anthracite-org/magnum-v2-72b", + "family": "llama", + "reasoning": false, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 16384, + "input": 16384, + "output": 8192 + } + }, + "nousresearch 2/hermes-4-405b": { + "id": "NousResearch 2/hermes-4-405b", + "family": "nousresearch", + "reasoning": false, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "input": 128000, + "output": 8192 + } + }, + "nousresearch 2/hermes-3-llama-3.1-70b": { + "id": "NousResearch 2/hermes-3-llama-3.1-70b", + "family": "nousresearch", + "reasoning": false, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 65536, + "input": 65536, + "output": 8192 + } + }, + "nousresearch 2/deephermes-3-mistral-24b-preview": { + "id": "NousResearch 2/DeepHermes-3-Mistral-24B-Preview", + "family": "nousresearch", + "reasoning": false, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "input": 128000, + "output": 32768 + } + }, + "nousresearch 2/hermes-4-70b": { + "id": "NousResearch 2/hermes-4-70b", + "family": "nousresearch", + "reasoning": false, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "input": 128000, + "output": 8192 + } + }, + "nousresearch 2/hermes-4-405b:thinking": { + "id": "NousResearch 2/hermes-4-405b:thinking", + "family": "nousresearch", + "reasoning": false, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "input": 128000, + "output": 8192 + } + }, + "nousresearch 2/hermes-4-70b:thinking": { + "id": "NousResearch 2/Hermes-4-70B:thinking", + "family": "nousresearch", + "reasoning": false, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "input": 128000, + "output": 8192 + } + }, + "pamanseau/openreasoning-nemotron-32b": { + "id": "pamanseau/OpenReasoning-Nemotron-32B", + "family": "nemotron", + "reasoning": true, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 32768, + "input": 32768, + "output": 65536 + } + }, + "deepseek-ai/deepseek-v3.2-exp-thinking": { + "id": "deepseek-ai/deepseek-v3.2-exp-thinking", + "family": "deepseek-thinking", + "reasoning": true, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 163840, + "input": 163840, + "output": 65536 + } + }, + "deepseek-ai/deepseek-v3.1:thinking": { + "id": "deepseek-ai/DeepSeek-V3.1:thinking", + "family": "deepseek-thinking", + "reasoning": false, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "input": 128000, + "output": 65536 + } + }, + "deepseek-ai/deepseek-v3.1-terminus:thinking": { + "id": "deepseek-ai/DeepSeek-V3.1-Terminus:thinking", + "family": "deepseek-thinking", + "reasoning": false, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "input": 128000, + "output": 65536 + } + }, + "raifle/sorcererlm-8x22b": { + "id": "raifle/sorcererlm-8x22b", + "family": "mixtral", + "reasoning": false, + "toolCall": false, + "modalities": { + "input": [ + "text", + "pdf" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 16000, + "input": 16000, + "output": 8192 + } + }, + "mlabonne/neuraldaredevil-8b-abliterated": { + "id": "mlabonne/NeuralDaredevil-8B-abliterated", + "family": "llama", + "reasoning": false, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 8192, + "input": 8192, + "output": 8192 + } + }, + "unsloth/gemma-3-1b-it": { + "id": "unsloth/gemma-3-1b-it", + "family": "unsloth", + "reasoning": false, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "input": 128000, + "output": 8192 + } + }, + "unsloth/gemma-3-12b-it": { + "id": "unsloth/gemma-3-12b-it", + "family": "unsloth", + "reasoning": false, + "toolCall": false, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 131072, + "input": 128000, + "output": 131072 + }, + "temperature": true + }, + "unsloth/gemma-3-4b-it": { + "id": "unsloth/gemma-3-4b-it", + "family": "unsloth", + "reasoning": false, + "toolCall": false, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 96000, + "input": 128000, + "output": 96000 + }, + "temperature": true + }, + "unsloth/gemma-3-27b-it": { + "id": "unsloth/gemma-3-27b-it", + "family": "unsloth", + "reasoning": false, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "input": 128000, + "output": 65536 + }, + "temperature": true + }, + "meituan-longcat/longcat-flash-chat-fp8": { + "id": "meituan-longcat/LongCat-Flash-Chat-FP8", + "family": "longcat", + "reasoning": false, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "input": 128000, + "output": 32768 + } + }, + "cognitivecomputations/dolphin-2.9.2-qwen2-72b": { + "id": "cognitivecomputations/dolphin-2.9.2-qwen2-72b", + "family": "qwen", + "reasoning": false, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 8192, + "input": 8192, + "output": 4096 + } + }, + "infermatic/mn-12b-inferor-v0.0": { + "id": "Infermatic/MN-12B-Inferor-v0.0", + "family": "mistral-nemo", + "reasoning": false, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 16384, + "input": 16384, + "output": 8192 + } + }, + "cruciblelab/l3.3-70b-loki-v2.0": { + "id": "CrucibleLab/L3.3-70B-Loki-V2.0", + "family": "llama", + "reasoning": false, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 16384, + "input": 16384, + "output": 16384 + } + }, + "soob3123/veiled-calla-12b": { + "id": "soob3123/Veiled-Calla-12B", + "family": "llama", + "reasoning": false, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 32768, + "input": 32768, + "output": 8192 + } + }, + "soob3123/amoral-gemma3-27b-v2": { + "id": "soob3123/amoral-gemma3-27B-v2", + "family": "gemma", + "reasoning": false, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 32768, + "input": 32768, + "output": 8192 + } + }, + "soob3123/grayline-qwen3-8b": { + "id": "soob3123/GrayLine-Qwen3-8B", + "family": "qwen", + "reasoning": false, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 16384, + "input": 16384, + "output": 32768 + } + }, + "neversleep/llama-3-lumimaid-70b-v0.1": { + "id": "NeverSleep/Llama-3-Lumimaid-70B-v0.1", + "family": "llama", + "reasoning": false, + "toolCall": false, + "modalities": { + "input": [ + "text", + "pdf" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 16384, + "input": 16384, + "output": 8192 + } + }, + "neversleep/lumimaid-v0.2-70b": { + "id": "NeverSleep/Lumimaid-v0.2-70B", + "family": "llama", + "reasoning": false, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 16384, + "input": 16384, + "output": 8192 + } + }, + "deepseek/deepseek-v3.2:thinking": { + "id": "deepseek/deepseek-v3.2:thinking", + "family": "deepseek", + "reasoning": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "pdf" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 163000, + "input": 163000, + "output": 65536 + } + }, + "marinaraspaghetti/nemomix-unleashed-12b": { + "id": "MarinaraSpaghetti/NemoMix-Unleashed-12B", + "family": "mistral-nemo", + "reasoning": false, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 32768, + "input": 32768, + "output": 8192 + } + }, + "moonshotai/kimi-k2.5:thinking": { + "id": "moonshotai/kimi-k2.5:thinking", + "family": "kimi-thinking", + "reasoning": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 256000, + "input": 256000, + "output": 65536 + } + }, + "moonshotai/kimi-k2-thinking-turbo-original": { + "id": "moonshotai/kimi-k2-thinking-turbo-original", + "family": "kimi-thinking", + "reasoning": true, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 256000, + "input": 256000, + "output": 16384 + } + }, + "moonshotai/kimi-k2-instruct-0711": { + "id": "moonshotai/kimi-k2-instruct-0711", + "family": "kimi", + "reasoning": false, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "input": 128000, + "output": 8192 + } + }, + "moonshotai/kimi-dev-72b": { + "id": "moonshotai/Kimi-Dev-72B", + "family": "kimi", + "reasoning": false, + "toolCall": false, + "modalities": { + "input": [ + "text", + "pdf" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "input": 128000, + "output": 131072 + } + }, + "moonshotai/kimi-k2-thinking-original": { + "id": "moonshotai/kimi-k2-thinking-original", + "family": "kimi-thinking", + "reasoning": true, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 256000, + "input": 256000, + "output": 16384 + } + }, + "google/gemini-flash-1.5": { + "id": "google/gemini-flash-1.5", + "family": "gemini-flash", + "reasoning": false, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 2000000, + "input": 2000000, + "output": 8192 + } + }, + "google/gemini-3-flash-preview-thinking": { + "id": "google/gemini-3-flash-preview-thinking", + "family": "gemini-flash", + "reasoning": true, + "toolCall": false, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 1048756, + "input": 1048756, + "output": 65536 + } + }, + "z-ai/glm-4.6:thinking": { + "id": "z-ai/glm-4.6:thinking", + "family": "glm", + "reasoning": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 200000, + "input": 200000, + "output": 65535 + } + }, + "z-ai/glm-4.5v:thinking": { + "id": "z-ai/glm-4.5v:thinking", + "family": "glmv", + "reasoning": true, + "toolCall": false, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 64000, + "input": 64000, + "output": 96000 + } + }, + "stepfun-ai/step-3.5-flash:thinking": { + "id": "stepfun-ai/step-3.5-flash:thinking", + "family": "step", + "reasoning": true, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 256000, + "input": 256000, + "output": 256000 + } + }, + "deepcogito/cogito-v1-preview-qwen-32b": { + "id": "deepcogito/cogito-v1-preview-qwen-32B", + "family": "qwen", + "reasoning": false, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "input": 128000, + "output": 32768 + } + }, + "inflatebot/mn-12b-mag-mell-r1": { + "id": "inflatebot/MN-12B-Mag-Mell-R1", + "family": "mistral-nemo", + "reasoning": false, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 16384, + "input": 16384, + "output": 8192 + } + }, + "nothingiisreal/l3.1-70b-celeste-v0.1-bf16": { + "id": "nothingiisreal/L3.1-70B-Celeste-V0.1-BF16", + "family": "llama", + "reasoning": false, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 16384, + "input": 16384, + "output": 16384 + } + }, + "x-ai/grok-4-fast:thinking": { + "id": "x-ai/grok-4-fast:thinking", + "family": "grok", + "reasoning": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 2000000, + "input": 2000000, + "output": 131072 + } + }, + "x-ai/grok-4-07-09": { + "id": "x-ai/grok-4-07-09", + "family": "grok", + "reasoning": true, + "toolCall": false, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 256000, + "input": 256000, + "output": 131072 + } + }, + "tngtech/deepseek-tng-r1t2-chimera": { + "id": "tngtech/DeepSeek-TNG-R1T2-Chimera", + "family": "tngtech", + "reasoning": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 163840, + "input": 128000, + "output": 163840 + }, + "temperature": true + }, + "tngtech/tng-r1t-chimera": { + "id": "tngtech/tng-r1t-chimera", + "family": "tngtech", + "reasoning": false, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "input": 128000, + "output": 65536 + } + }, + "mistralai/mixtral-8x22b-instruct-v0.1": { + "id": "mistralai/mixtral-8x22b-instruct-v0.1", + "family": "mixtral", + "reasoning": false, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 65536, + "input": 65536, + "output": 32768 + } + }, + "mistralai/mistral-tiny": { + "id": "mistralai/mistral-tiny", + "family": "mistral", + "reasoning": false, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 32000, + "input": 32000, + "output": 8192 + } + }, + "mistralai/mistral-7b-instruct": { + "id": "mistralai/mistral-7b-instruct", + "family": "mistral", + "reasoning": false, + "toolCall": false, + "modalities": { + "input": [ + "text", + "pdf" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 32768, + "input": 32768, + "output": 8192 + } + }, + "mistralai/mixtral-8x7b-instruct-v0.1": { + "id": "mistralai/mixtral-8x7b-instruct-v0.1", + "family": "mixtral", + "reasoning": false, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 32768, + "input": 32768, + "output": 32768 + } + }, + "tongyi-zhiwen/qwenlong-l1-32b": { + "id": "Tongyi-Zhiwen/QwenLong-L1-32B", + "family": "qwen", + "reasoning": false, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "input": 128000, + "output": 40960 + } + }, + "readyart/the-omega-abomination-l-70b-v1.0": { + "id": "ReadyArt/The-Omega-Abomination-L-70B-v1.0", + "family": "llama", + "reasoning": false, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 16384, + "input": 16384, + "output": 16384 + } + }, + "readyart/ms3.2-the-omega-directive-24b-unslop-v2.0": { + "id": "ReadyArt/MS3.2-The-Omega-Directive-24B-Unslop-v2.0", + "family": "llama", + "reasoning": false, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 16384, + "input": 16384, + "output": 32768 + } + }, + "openai/gpt-5.1-2025-11-13": { + "id": "openai/gpt-5.1-2025-11-13", + "family": "gpt", + "reasoning": false, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 1000000, + "input": 1000000, + "output": 32768 + } + }, + "openai/gpt-5-chat-latest": { + "id": "openai/gpt-5-chat-latest", + "family": "gpt", + "reasoning": true, + "toolCall": false, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 400000, + "input": 400000, + "output": 128000 + } + }, + "openai/o3-mini-low": { + "id": "openai/o3-mini-low", + "family": "o-mini", + "reasoning": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 200000, + "input": 200000, + "output": 100000 + } + }, + "openai/o3-pro-2025-06-10": { + "id": "openai/o3-pro-2025-06-10", + "family": "o-pro", + "reasoning": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 200000, + "input": 200000, + "output": 100000 + } + }, + "openai/gpt-5.1-chat-latest": { + "id": "openai/gpt-5.1-chat-latest", + "family": "gpt", + "reasoning": true, + "toolCall": false, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 400000, + "input": 400000, + "output": 16384 + } + }, + "vongolachouko/starcannon-unleashed-12b-v1.0": { + "id": "VongolaChouko/Starcannon-Unleashed-12B-v1.0", + "family": "mistral-nemo", + "reasoning": false, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 16384, + "input": 16384, + "output": 8192 + } + }, + "cohere/command-r": { + "id": "cohere/command-r", + "family": "command-r", + "reasoning": false, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "input": 128000, + "output": 4096 + } + }, + "thudm/glm-z1-rumination-32b-0414": { + "id": "THUDM/GLM-Z1-Rumination-32B-0414", + "family": "glm-z", + "reasoning": false, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 32000, + "input": 32000, + "output": 65536 + } + }, + "chutesai/mistral-small-3.2-24b-instruct-2506": { + "id": "chutesai/Mistral-Small-3.2-24B-Instruct-2506", + "family": "chutesai", + "reasoning": false, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 131072, + "input": 128000, + "output": 131072 + }, + "temperature": true + }, + "baseten/kimi-k2-instruct-fp4": { + "id": "baseten/Kimi-K2-Instruct-FP4", + "family": "kimi", + "reasoning": false, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "input": 128000, + "output": 131072 + } + }, + "galrionsoftworks/mn-loosecannon-12b-v1": { + "id": "GalrionSoftworks/MN-LooseCannon-12B-v1", + "family": "mistral-nemo", + "reasoning": false, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 16384, + "input": 16384, + "output": 8192 + } + }, + "alibaba-nlp/tongyi-deepresearch-30b-a3b": { + "id": "Alibaba-NLP/Tongyi-DeepResearch-30B-A3B", + "family": "yi", + "reasoning": false, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "input": 128000, + "output": 65536 + } + }, + "steelskull/l3.3-electra-r1-70b": { + "id": "Steelskull/L3.3-Electra-R1-70b", + "family": "llama", + "reasoning": false, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 16384, + "input": 16384, + "output": 16384 + } + }, + "steelskull/l3.3-ms-evalebis-70b": { + "id": "Steelskull/L3.3-MS-Evalebis-70b", + "family": "llama", + "reasoning": false, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 16384, + "input": 16384, + "output": 16384 + } + }, + "steelskull/l3.3-cu-mai-r1-70b": { + "id": "Steelskull/L3.3-Cu-Mai-R1-70b", + "family": "llama", + "reasoning": false, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 16384, + "input": 16384, + "output": 16384 + } + }, + "steelskull/l3.3-nevoria-r1-70b": { + "id": "Steelskull/L3.3-Nevoria-R1-70b", + "family": "llama", + "reasoning": false, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 16384, + "input": 16384, + "output": 16384 + } + }, + "steelskull/l3.3-ms-nevoria-70b": { + "id": "Steelskull/L3.3-MS-Nevoria-70b", + "family": "llama", + "reasoning": false, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 16384, + "input": 16384, + "output": 16384 + } + }, + "steelskull/l3.3-ms-evayale-70b": { + "id": "Steelskull/L3.3-MS-Evayale-70B", + "family": "llama", + "reasoning": false, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 16384, + "input": 16384, + "output": 16384 + } + }, + "salesforce/llama-xlam-2-70b-fc-r": { + "id": "Salesforce/Llama-xLAM-2-70b-fc-r", + "family": "llama", + "reasoning": false, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "input": 128000, + "output": 16384 + } + }, + "latitudegames/wayfarer-large-70b-llama-3.3": { + "id": "LatitudeGames/Wayfarer-Large-70B-Llama-3.3", + "family": "llama", + "reasoning": false, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 16384, + "input": 16384, + "output": 16384 + } + }, + "thedrummer 2/cydonia-24b-v4.3": { + "id": "TheDrummer 2/Cydonia-24B-v4.3", + "family": "llama", + "reasoning": false, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 32768, + "input": 32768, + "output": 32768 + } + }, + "thedrummer 2/anubis-70b-v1": { + "id": "TheDrummer 2/Anubis-70B-v1", + "family": "llama", + "reasoning": false, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 65536, + "input": 65536, + "output": 16384 + } + }, + "thedrummer 2/cydonia-24b-v4": { + "id": "TheDrummer 2/Cydonia-24B-v4", + "family": "llama", + "reasoning": false, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 16384, + "input": 16384, + "output": 32768 + } + }, + "thedrummer 2/magidonia-24b-v4.3": { + "id": "TheDrummer 2/Magidonia-24B-v4.3", + "family": "llama", + "reasoning": false, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 32768, + "input": 32768, + "output": 32768 + } + }, + "thedrummer 2/anubis-70b-v1.1": { + "id": "TheDrummer 2/Anubis-70B-v1.1", + "family": "llama", + "reasoning": false, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 131072, + "input": 131072, + "output": 16384 + } + }, + "thedrummer 2/rocinante-12b-v1.1": { + "id": "TheDrummer 2/Rocinante-12B-v1.1", + "family": "llama", + "reasoning": false, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 16384, + "input": 16384, + "output": 8192 + } + }, + "thedrummer 2/cydonia-24b-v2": { + "id": "TheDrummer 2/Cydonia-24B-v2", + "family": "llama", + "reasoning": false, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 16384, + "input": 16384, + "output": 32768 + } + }, + "thedrummer 2/skyfall-36b-v2": { + "id": "TheDrummer 2/skyfall-36b-v2", + "family": "llama", + "reasoning": false, + "toolCall": false, + "modalities": { + "input": [ + "text", + "pdf" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 64000, + "input": 64000, + "output": 32768 + } + }, + "thedrummer 2/unslopnemo-12b-v4.1": { + "id": "TheDrummer 2/UnslopNemo-12B-v4.1", + "family": "llama", + "reasoning": false, + "toolCall": false, + "modalities": { + "input": [ + "text", + "pdf" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 32768, + "input": 32768, + "output": 8192 + } + }, + "thedrummer 2/cydonia-24b-v4.1": { + "id": "TheDrummer 2/Cydonia-24B-v4.1", + "family": "llama", + "reasoning": false, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 16384, + "input": 16384, + "output": 32768 + } + }, + "shisa-ai/shisa-v2.1-llama3.3-70b": { + "id": "shisa-ai/shisa-v2.1-llama3.3-70b", + "family": "llama", + "reasoning": false, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 32768, + "input": 32768, + "output": 4096 + } + }, + "shisa-ai/shisa-v2-llama3.3-70b": { + "id": "shisa-ai/shisa-v2-llama3.3-70b", + "family": "llama", + "reasoning": false, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "input": 128000, + "output": 16384 + } + }, + "anthropic/claude-sonnet-4.6:thinking": { + "id": "anthropic/claude-sonnet-4.6:thinking", + "family": "claude-sonnet", + "reasoning": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image", + "pdf" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 1000000, + "input": 1000000, + "output": 128000 + } + }, + "anthropic/claude-opus-4.6:thinking:low": { + "id": "anthropic/claude-opus-4.6:thinking:low", + "family": "claude-opus", + "reasoning": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image", + "pdf" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 1000000, + "input": 1000000, + "output": 128000 + } + }, + "anthropic/claude-opus-4.6:thinking": { + "id": "anthropic/claude-opus-4.6:thinking", + "family": "claude-opus", + "reasoning": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image", + "pdf" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 1000000, + "input": 1000000, + "output": 128000 + } + }, + "anthropic/claude-opus-4.6:thinking:medium": { + "id": "anthropic/claude-opus-4.6:thinking:medium", + "family": "claude-opus", + "reasoning": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image", + "pdf" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 1000000, + "input": 1000000, + "output": 128000 + } + }, + "anthropic/claude-opus-4.6:thinking:max": { + "id": "anthropic/claude-opus-4.6:thinking:max", + "family": "claude-opus", + "reasoning": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image", + "pdf" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 1000000, + "input": 1000000, + "output": 128000 + } + }, + "miromind-ai/mirothinker-v1.5-235b": { + "id": "miromind-ai/MiroThinker-v1.5-235B", + "family": "gpt", + "reasoning": false, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 262144, + "input": 32768, + "output": 8192 + }, + "temperature": true + }, + "sao10k/l3.3-70b-euryale-v2.3": { + "id": "Sao10K/L3.3-70B-Euryale-v2.3", + "family": "llama", + "reasoning": false, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 20480, + "input": 20480, + "output": 16384 + } + }, + "sao10k/l3.1-70b-euryale-v2.2": { + "id": "Sao10K/L3.1-70B-Euryale-v2.2", + "family": "llama", + "reasoning": false, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 20480, + "input": 20480, + "output": 16384 + } + }, + "huihui-ai/deepseek-r1-distill-llama-70b-abliterated": { + "id": "huihui-ai/DeepSeek-R1-Distill-Llama-70B-abliterated", + "family": "deepseek", + "reasoning": true, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 16384, + "input": 16384, + "output": 8192 + } + }, + "huihui-ai/qwen2.5-32b-instruct-abliterated": { + "id": "huihui-ai/Qwen2.5-32B-Instruct-abliterated", + "family": "qwen", + "reasoning": false, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 32768, + "input": 32768, + "output": 8192 + } + }, + "huihui-ai/deepseek-r1-distill-qwen-32b-abliterated": { + "id": "huihui-ai/DeepSeek-R1-Distill-Qwen-32B-abliterated", + "family": "qwen", + "reasoning": true, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 16384, + "input": 16384, + "output": 8192 + } + }, + "huihui-ai/llama-3.3-70b-instruct-abliterated": { + "id": "huihui-ai/Llama-3.3-70B-Instruct-abliterated", + "family": "llama", + "reasoning": false, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 16384, + "input": 16384, + "output": 16384 + } + }, + "huihui-ai/llama-3.1-nemotron-70b-instruct-hf-abliterated": { + "id": "huihui-ai/Llama-3.1-Nemotron-70B-Instruct-HF-abliterated", + "family": "nemotron", + "reasoning": false, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 16384, + "input": 16384, + "output": 16384 + } + }, + "dmind/dmind-1-mini": { + "id": "dmind/dmind-1-mini", + "family": "gpt", + "reasoning": false, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 32768, + "input": 32768, + "output": 8192 + } + }, + "dmind/dmind-1": { + "id": "dmind/dmind-1", + "family": "gpt", + "reasoning": false, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 32768, + "input": 32768, + "output": 8192 + } + }, + "eva-unit-01/eva-qwen2.5-72b-v0.2": { + "id": "EVA-UNIT-01/EVA-Qwen2.5-72B-v0.2", + "family": "qwen", + "reasoning": false, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "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", + "family": "llama", + "reasoning": false, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 16384, + "input": 16384, + "output": 16384 + } + }, + "eva-unit-01/eva-llama-3.33-70b-v0.1": { + "id": "EVA-UNIT-01/EVA-LLaMA-3.33-70B-v0.1", + "family": "llama", + "reasoning": false, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "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", + "family": "qwen", + "reasoning": false, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 16384, + "input": 16384, + "output": 8192 + } + }, + "qwen-3-235b-a22b-instruct-2507": { + "id": "qwen-3-235b-a22b-instruct-2507", + "family": "qwen", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 131000, + "output": 32000 + } + }, + "llama3.1-8b": { + "id": "llama3.1-8b", + "family": "llama", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 32000, + "output": 8000 + } + }, + "zai-glm-4.7": { + "id": "zai-glm-4.7", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 131072, + "output": 40000 + } + }, + "gpt-5.3-chat": { + "id": "gpt-5.3-chat", + "family": "gpt-codex", + "reasoning": true, + "temperature": false, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 16384 + } + }, + "kimi-k2-instruct": { + "id": "kimi-k2-instruct", + "family": "kimi", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 131000, + "output": 131000 + } + }, + "claude-opus4-6": { + "id": "claude-opus4-6", + "family": "claude-opus", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image", + "pdf" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 1000000, + "output": 1000000 + } + }, + "claude-4-6-sonnet": { + "id": "claude-4-6-sonnet", + "family": "claude-sonnet", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image", + "pdf" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 1000000, + "output": 1000000 + } + }, + "devstral-small-2512": { + "id": "devstral-small-2512", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 262000, + "output": 262000 + } + }, + "intellect-3": { + "id": "intellect-3", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 128000 + } + }, + "nova-pro-v1": { + "id": "nova-pro-v1", + "family": "nova-pro", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 300000, + "output": 5000 + } + }, + "llama-3.1-405b-instruct": { + "id": "llama-3.1-405b-instruct", + "family": "llama", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 128000 + } + }, + "claude-opus4-5": { + "id": "claude-opus4-5", + "family": "claude-opus", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image", + "pdf" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 200000, + "output": 200000 + } + }, + "claude-4-5-sonnet": { + "id": "claude-4-5-sonnet", + "family": "claude-sonnet", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image", + "pdf" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 200000, + "output": 200000 + } + }, + "grok-2-1212": { + "id": "grok-2-1212", + "family": "grok", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 131072, + "output": 8192 + } + }, + "grok-4.20-multi-agent-0309": { + "id": "grok-4.20-multi-agent-0309", + "family": "grok", + "reasoning": true, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 2000000, + "output": 30000 + } + }, + "grok-2": { + "id": "grok-2", + "family": "grok", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 131072, + "output": 8192 + } + }, + "grok-3-fast-latest": { + "id": "grok-3-fast-latest", + "family": "grok", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 131072, + "output": 8192 + } + }, + "grok-2-vision": { + "id": "grok-2-vision", + "family": "grok", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 8192, + "output": 4096 + } + }, + "grok-2-vision-1212": { + "id": "grok-2-vision-1212", + "family": "grok", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 8192, + "output": 4096 + } + }, + "grok-beta": { + "id": "grok-beta", + "family": "grok-beta", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 131072, + "output": 4096 + } + }, + "grok-3-mini-fast": { + "id": "grok-3-mini-fast", + "family": "grok", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 131072, + "output": 8192 + } + }, + "grok-4-fast": { + "id": "grok-4-fast", + "family": "grok", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 2000000, + "output": 30000 + } + }, + "grok-3-latest": { + "id": "grok-3-latest", + "family": "grok", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 131072, + "output": 8192 + } + }, + "grok-4-1-fast": { + "id": "grok-4-1-fast", + "family": "grok", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 2000000, + "output": 30000 + } + }, + "grok-2-vision-latest": { + "id": "grok-2-vision-latest", + "family": "grok", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 8192, + "output": 4096 + } + }, + "grok-3-mini-latest": { + "id": "grok-3-mini-latest", + "family": "grok", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 131072, + "output": 8192 + } + }, + "grok-3-mini-fast-latest": { + "id": "grok-3-mini-fast-latest", + "family": "grok", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 131072, + "output": 8192 + } + }, + "grok-4.20-0309-reasoning": { + "id": "grok-4.20-0309-reasoning", + "family": "grok", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 2000000, + "output": 30000 + } + }, + "grok-2-latest": { + "id": "grok-2-latest", + "family": "grok", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 131072, + "output": 8192 + } + }, + "grok-vision-beta": { + "id": "grok-vision-beta", + "family": "grok-vision", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 8192, + "output": 4096 + } + }, + "grok-4.20-0309-non-reasoning": { + "id": "grok-4.20-0309-non-reasoning", + "family": "grok", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 2000000, + "output": 30000 + } + }, + "grok-3-fast": { + "id": "grok-3-fast", + "family": "grok", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 131072, + "output": 8192 + } + }, + "qwen-math-plus": { + "id": "qwen-math-plus", + "family": "qwen", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 4096, + "output": 3072 + } + }, + "deepseek-v3-1": { + "id": "deepseek-v3-1", + "family": "deepseek", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 131072, + "output": 65536 + } + }, + "qwen2-5-coder-7b-instruct": { + "id": "qwen2-5-coder-7b-instruct", + "family": "qwen", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 131072, + "output": 8192 + } + }, + "deepseek-r1-distill-qwen-14b": { + "id": "deepseek-r1-distill-qwen-14b", + "family": "qwen", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 32768, + "output": 16384 + } + }, + "moonshot-kimi-k2-instruct": { + "id": "moonshot-kimi-k2-instruct", + "family": "kimi", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 131072, + "output": 8192 + } + }, + "qwen-doc-turbo": { + "id": "qwen-doc-turbo", + "family": "qwen", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 131072, + "output": 8192 + } + }, + "tongyi-intent-detect-v3": { + "id": "tongyi-intent-detect-v3", + "family": "yi", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 8192, + "output": 1024 + } + }, + "qwen-plus-character": { + "id": "qwen-plus-character", + "family": "qwen", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 32768, + "output": 4096 + } + }, + "deepseek-v3-2-exp": { + "id": "deepseek-v3-2-exp", + "family": "deepseek", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 131072, + "output": 65536 + } + }, + "deepseek-r1-distill-llama-8b": { + "id": "deepseek-r1-distill-llama-8b", + "family": "deepseek-thinking", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 32768, + "output": 16384 + } + }, + "qwen3.5-flash": { + "id": "qwen3.5-flash", + "family": "qwen", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image", + "video" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 1000000, + "output": 65536 + } + }, + "qwen2-5-math-7b-instruct": { + "id": "qwen2-5-math-7b-instruct", + "family": "qwen", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 4096, + "output": 3072 + } + }, + "deepseek-r1-distill-qwen-1-5b": { + "id": "deepseek-r1-distill-qwen-1-5b", + "family": "qwen", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 32768, + "output": 16384 + } + }, + "deepseek-r1-distill-qwen-7b": { + "id": "deepseek-r1-distill-qwen-7b", + "family": "qwen", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 32768, + "output": 16384 + } + }, + "qwen-deep-research": { + "id": "qwen-deep-research", + "family": "qwen", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 1000000, + "output": 32768 + } + }, + "qwen2-5-math-72b-instruct": { + "id": "qwen2-5-math-72b-instruct", + "family": "qwen", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 4096, + "output": 3072 + } + }, + "qwen-math-turbo": { + "id": "qwen-math-turbo", + "family": "qwen", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 4096, + "output": 3072 + } + }, + "qwen2-5-coder-32b-instruct": { + "id": "qwen2-5-coder-32b-instruct", + "family": "qwen", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 131072, + "output": 8192 + } + }, + "kimi/kimi-k2.5": { + "id": "kimi/kimi-k2.5", + "family": "kimi", + "reasoning": true, + "temperature": false, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image", + "video" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 262144, + "output": 262144 + } + }, + "siliconflow/deepseek-r1-0528": { + "id": "siliconflow/deepseek-r1-0528", + "family": "deepseek-thinking", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 163840, + "output": 32768 + } + }, + "siliconflow/deepseek-v3-0324": { + "id": "siliconflow/deepseek-v3-0324", + "family": "deepseek", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 163840, + "output": 163840 + } + }, + "siliconflow/deepseek-v3.1-terminus": { + "id": "siliconflow/deepseek-v3.1-terminus", + "family": "deepseek", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 163840, + "output": 65536 + } + }, + "siliconflow/deepseek-v3.2": { + "id": "siliconflow/deepseek-v3.2", + "family": "deepseek", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 163840, + "output": 65536 + } + }, + "zai-org/glm-4.7-tee": { + "id": "zai-org/GLM-4.7-TEE", + "family": "glm", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 202752, + "output": 65535 + } + }, + "zai-org/glm-4.6-tee": { + "id": "zai-org/GLM-4.6-TEE", + "family": "glm", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 202752, + "output": 65536 + } + }, + "zai-org/glm-5-tee": { + "id": "zai-org/GLM-5-TEE", + "family": "glm", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 202752, + "output": 65535 + } + }, + "zai-org/glm-4.6-fp8": { + "id": "zai-org/GLM-4.6-FP8", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 202752, + "output": 65535 + } + }, + "zai-org/glm-4.5-tee": { + "id": "zai-org/GLM-4.5-TEE", + "family": "glm", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 131072, + "output": 65536 + } + }, + "zai-org/glm-5-turbo": { + "id": "zai-org/GLM-5-Turbo", + "family": "glm", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 202752, + "output": 65535 + } + }, + "nvidia/nvidia-nemotron-3-nano-30b-a3b-bf16": { + "id": "nvidia/NVIDIA-Nemotron-3-Nano-30B-A3B-BF16", + "family": "nemotron", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 262144, + "output": 262144 + } + }, + "nousresearch/hermes-4.3-36b": { + "id": "NousResearch/Hermes-4.3-36B", + "family": "nousresearch", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 32768, + "output": 8192 + } + }, + "nousresearch/deephermes-3-mistral-24b-preview": { + "id": "NousResearch/DeepHermes-3-Mistral-24B-Preview", + "family": "nousresearch", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 32768, + "output": 32768 + } + }, + "nousresearch/hermes-4-14b": { + "id": "NousResearch/Hermes-4-14B", + "family": "nousresearch", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 40960, + "output": 40960 + } + }, + "nousresearch/hermes-4-405b-fp8-tee": { + "id": "NousResearch/Hermes-4-405B-FP8-TEE", + "family": "nousresearch", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 131072, + "output": 65536 + } + }, + "minimaxai/minimax-m2.5-tee": { + "id": "MiniMaxAI/MiniMax-M2.5-TEE", + "family": "minimax", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 196608, + "output": 65536 + } + }, + "minimaxai/minimax-m2.1-tee": { + "id": "MiniMaxAI/MiniMax-M2.1-TEE", + "family": "minimax", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 196608, + "output": 65536 + } + }, + "deepseek-ai/deepseek-v3.1-terminus-tee": { + "id": "deepseek-ai/DeepSeek-V3.1-Terminus-TEE", + "family": "deepseek", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 163840, + "output": 65536 + } + }, + "deepseek-ai/deepseek-v3.2-tee": { + "id": "deepseek-ai/DeepSeek-V3.2-TEE", + "family": "deepseek", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 131072, + "output": 65536 + } + }, + "deepseek-ai/deepseek-v3-0324-tee": { + "id": "deepseek-ai/DeepSeek-V3-0324-TEE", + "family": "deepseek", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 163840, + "output": 65536 + } + }, + "deepseek-ai/deepseek-v3.2-speciale-tee": { + "id": "deepseek-ai/DeepSeek-V3.2-Speciale-TEE", + "family": "deepseek", + "reasoning": true, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 163840, + "output": 65536 + } + }, + "deepseek-ai/deepseek-r1-tee": { + "id": "deepseek-ai/DeepSeek-R1-TEE", + "family": "deepseek-thinking", + "reasoning": true, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 163840, + "output": 163840 + } + }, + "deepseek-ai/deepseek-v3.1-tee": { + "id": "deepseek-ai/DeepSeek-V3.1-TEE", + "family": "deepseek", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 163840, + "output": 65536 + } + }, + "deepseek-ai/deepseek-r1-0528-tee": { + "id": "deepseek-ai/DeepSeek-R1-0528-TEE", + "family": "deepseek-thinking", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 163840, + "output": 65536 + } + }, + "rednote-hilab/dots.ocr": { + "id": "rednote-hilab/dots.ocr", + "family": "rednote", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 131072, + "output": 131072 + } + }, + "unsloth/mistral-nemo-instruct-2407": { + "id": "unsloth/Mistral-Nemo-Instruct-2407", + "family": "unsloth", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 131072, + "output": 131072 + } + }, + "unsloth/mistral-small-24b-instruct-2501": { + "id": "unsloth/Mistral-Small-24B-Instruct-2501", + "family": "unsloth", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 32768, + "output": 32768 + } + }, + "unsloth/llama-3.2-1b-instruct": { + "id": "unsloth/Llama-3.2-1B-Instruct", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 32768, + "output": 8192 + } + }, + "unsloth/llama-3.2-3b-instruct": { + "id": "unsloth/Llama-3.2-3B-Instruct", + "family": "unsloth", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 16384, + "output": 16384 + } + }, + "moonshotai/kimi-k2.5-tee": { + "id": "moonshotai/Kimi-K2.5-TEE", + "family": "kimi", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image", + "video" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 262144, + "output": 65535 + } + }, + "moonshotai/kimi-k2-thinking-tee": { + "id": "moonshotai/Kimi-K2-Thinking-TEE", + "family": "kimi-thinking", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 262144, + "output": 65535 + } + }, + "qwen/qwen3.5-397b-a17b-tee": { + "id": "Qwen/Qwen3.5-397B-A17B-TEE", + "family": "qwen", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 262144, + "output": 65536 + } + }, + "qwen/qwen3-coder-480b-a35b-instruct-fp8-tee": { + "id": "Qwen/Qwen3-Coder-480B-A35B-Instruct-FP8-TEE", + "family": "qwen", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 262144, + "output": 262144 + } + }, + "qwen/qwen3-235b-a22b-instruct-2507-tee": { + "id": "Qwen/Qwen3-235B-A22B-Instruct-2507-TEE", + "family": "qwen", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 262144, + "output": 65536 + } + }, + "qwen/qwen2.5-vl-72b-instruct-tee": { + "id": "Qwen/Qwen2.5-VL-72B-Instruct-TEE", + "family": "qwen", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 32768, + "output": 32768 + } + }, + "qwen/qwen3guard-gen-0.6b": { + "id": "Qwen/Qwen3Guard-Gen-0.6B", + "family": "qwen", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 32768, + "output": 8192 + } + }, + "tngtech/deepseek-r1t-chimera": { + "id": "tngtech/DeepSeek-R1T-Chimera", + "family": "tngtech", + "reasoning": true, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 163840, + "output": 163840 + } + }, + "tngtech/tng-r1t-chimera-turbo": { + "id": "tngtech/TNG-R1T-Chimera-Turbo", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 163840, + "output": 65536 + } + }, + "tngtech/tng-r1t-chimera-tee": { + "id": "tngtech/TNG-R1T-Chimera-TEE", + "family": "tngtech", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 163840, + "output": 65536 + } + }, + "mistralai/devstral-2-123b-instruct-2512-tee": { + "id": "mistralai/Devstral-2-123B-Instruct-2512-TEE", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 262144, + "output": 65536 + } + }, + "openai/gpt-oss-120b-tee": { + "id": "openai/gpt-oss-120b-TEE", + "family": "gpt-oss", + "reasoning": true, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 131072, + "output": 65536 + } + }, + "chutesai/mistral-small-3.1-24b-instruct-2503": { + "id": "chutesai/Mistral-Small-3.1-24B-Instruct-2503", + "family": "chutesai", + "reasoning": false, + "temperature": true, + "toolCall": true, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 131072, + "output": 131072 + } + }, + "opengvlab/internvl3-78b-tee": { + "id": "OpenGVLab/InternVL3-78B-TEE", + "family": "opengvlab", + "reasoning": false, + "temperature": true, + "toolCall": false, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 32768, + "output": 32768 + } + } + } +} diff --git a/src/hooks/auto-update-checker/hook.test.ts b/src/hooks/auto-update-checker/hook.test.ts index 49391cc2b..6f2f06e2e 100644 --- a/src/hooks/auto-update-checker/hook.test.ts +++ b/src/hooks/auto-update-checker/hook.test.ts @@ -3,6 +3,7 @@ import { afterEach, beforeEach, describe, expect, it, mock } from "bun:test" const mockShowConfigErrorsIfAny = mock(async () => {}) const mockShowModelCacheWarningIfNeeded = mock(async () => {}) const mockUpdateAndShowConnectedProvidersCacheStatus = mock(async () => {}) +const mockRefreshModelCapabilitiesOnStartup = mock(async () => {}) const mockShowLocalDevToast = mock(async () => {}) const mockShowVersionToast = mock(async () => {}) const mockRunBackgroundUpdateCheck = mock(async () => {}) @@ -22,6 +23,10 @@ mock.module("./hook/connected-providers-status", () => ({ mockUpdateAndShowConnectedProvidersCacheStatus, })) +mock.module("./hook/model-capabilities-status", () => ({ + refreshModelCapabilitiesOnStartup: mockRefreshModelCapabilitiesOnStartup, +})) + mock.module("./hook/startup-toasts", () => ({ showLocalDevToast: mockShowLocalDevToast, showVersionToast: mockShowVersionToast, @@ -78,6 +83,7 @@ beforeEach(() => { mockShowConfigErrorsIfAny.mockClear() mockShowModelCacheWarningIfNeeded.mockClear() mockUpdateAndShowConnectedProvidersCacheStatus.mockClear() + mockRefreshModelCapabilitiesOnStartup.mockClear() mockShowLocalDevToast.mockClear() mockShowVersionToast.mockClear() mockRunBackgroundUpdateCheck.mockClear() @@ -112,6 +118,7 @@ describe("createAutoUpdateCheckerHook", () => { expect(mockShowConfigErrorsIfAny).not.toHaveBeenCalled() expect(mockShowModelCacheWarningIfNeeded).not.toHaveBeenCalled() expect(mockUpdateAndShowConnectedProvidersCacheStatus).not.toHaveBeenCalled() + expect(mockRefreshModelCapabilitiesOnStartup).not.toHaveBeenCalled() expect(mockShowLocalDevToast).not.toHaveBeenCalled() expect(mockShowVersionToast).not.toHaveBeenCalled() expect(mockRunBackgroundUpdateCheck).not.toHaveBeenCalled() @@ -129,6 +136,7 @@ describe("createAutoUpdateCheckerHook", () => { //#then - startup checks, toast, and background check run expect(mockShowConfigErrorsIfAny).toHaveBeenCalledTimes(1) expect(mockUpdateAndShowConnectedProvidersCacheStatus).toHaveBeenCalledTimes(1) + expect(mockRefreshModelCapabilitiesOnStartup).toHaveBeenCalledTimes(1) expect(mockShowModelCacheWarningIfNeeded).toHaveBeenCalledTimes(1) expect(mockShowVersionToast).toHaveBeenCalledTimes(1) expect(mockRunBackgroundUpdateCheck).toHaveBeenCalledTimes(1) @@ -146,6 +154,7 @@ describe("createAutoUpdateCheckerHook", () => { //#then - no startup actions run expect(mockShowConfigErrorsIfAny).not.toHaveBeenCalled() expect(mockUpdateAndShowConnectedProvidersCacheStatus).not.toHaveBeenCalled() + expect(mockRefreshModelCapabilitiesOnStartup).not.toHaveBeenCalled() expect(mockShowModelCacheWarningIfNeeded).not.toHaveBeenCalled() expect(mockShowLocalDevToast).not.toHaveBeenCalled() expect(mockShowVersionToast).not.toHaveBeenCalled() @@ -165,6 +174,7 @@ describe("createAutoUpdateCheckerHook", () => { //#then - side effects execute only once expect(mockShowConfigErrorsIfAny).toHaveBeenCalledTimes(1) expect(mockUpdateAndShowConnectedProvidersCacheStatus).toHaveBeenCalledTimes(1) + expect(mockRefreshModelCapabilitiesOnStartup).toHaveBeenCalledTimes(1) expect(mockShowModelCacheWarningIfNeeded).toHaveBeenCalledTimes(1) expect(mockShowVersionToast).toHaveBeenCalledTimes(1) expect(mockRunBackgroundUpdateCheck).toHaveBeenCalledTimes(1) @@ -183,6 +193,7 @@ describe("createAutoUpdateCheckerHook", () => { //#then - local dev toast is shown and background check is skipped expect(mockShowConfigErrorsIfAny).toHaveBeenCalledTimes(1) expect(mockUpdateAndShowConnectedProvidersCacheStatus).toHaveBeenCalledTimes(1) + expect(mockRefreshModelCapabilitiesOnStartup).toHaveBeenCalledTimes(1) expect(mockShowModelCacheWarningIfNeeded).toHaveBeenCalledTimes(1) expect(mockShowLocalDevToast).toHaveBeenCalledTimes(1) expect(mockShowVersionToast).not.toHaveBeenCalled() @@ -205,6 +216,7 @@ describe("createAutoUpdateCheckerHook", () => { //#then - no startup actions run expect(mockShowConfigErrorsIfAny).not.toHaveBeenCalled() expect(mockUpdateAndShowConnectedProvidersCacheStatus).not.toHaveBeenCalled() + expect(mockRefreshModelCapabilitiesOnStartup).not.toHaveBeenCalled() expect(mockShowModelCacheWarningIfNeeded).not.toHaveBeenCalled() expect(mockShowLocalDevToast).not.toHaveBeenCalled() expect(mockShowVersionToast).not.toHaveBeenCalled() diff --git a/src/hooks/auto-update-checker/hook.ts b/src/hooks/auto-update-checker/hook.ts index b915f9e55..caac8ddc5 100644 --- a/src/hooks/auto-update-checker/hook.ts +++ b/src/hooks/auto-update-checker/hook.ts @@ -5,11 +5,17 @@ import type { AutoUpdateCheckerOptions } from "./types" import { runBackgroundUpdateCheck } from "./hook/background-update-check" import { showConfigErrorsIfAny } from "./hook/config-errors-toast" import { updateAndShowConnectedProvidersCacheStatus } from "./hook/connected-providers-status" +import { refreshModelCapabilitiesOnStartup } from "./hook/model-capabilities-status" import { showModelCacheWarningIfNeeded } from "./hook/model-cache-warning" import { showLocalDevToast, showVersionToast } from "./hook/startup-toasts" export function createAutoUpdateCheckerHook(ctx: PluginInput, options: AutoUpdateCheckerOptions = {}) { - const { showStartupToast = true, isSisyphusEnabled = false, autoUpdate = true } = options + const { + showStartupToast = true, + isSisyphusEnabled = false, + autoUpdate = true, + modelCapabilities, + } = options const isCliRunMode = process.env.OPENCODE_CLI_RUN_MODE === "true" const getToastMessage = (isUpdate: boolean, latestVersion?: string): string => { @@ -43,6 +49,7 @@ export function createAutoUpdateCheckerHook(ctx: PluginInput, options: AutoUpdat await showConfigErrorsIfAny(ctx) await updateAndShowConnectedProvidersCacheStatus(ctx) + await refreshModelCapabilitiesOnStartup(modelCapabilities) await showModelCacheWarningIfNeeded(ctx) if (localDevVersion) { diff --git a/src/hooks/auto-update-checker/hook/model-capabilities-status.ts b/src/hooks/auto-update-checker/hook/model-capabilities-status.ts new file mode 100644 index 000000000..bead830b4 --- /dev/null +++ b/src/hooks/auto-update-checker/hook/model-capabilities-status.ts @@ -0,0 +1,37 @@ +import type { ModelCapabilitiesConfig } from "../../../config/schema/model-capabilities" +import { refreshModelCapabilitiesCache } from "../../../shared/model-capabilities-cache" +import { log } from "../../../shared/logger" + +const DEFAULT_REFRESH_TIMEOUT_MS = 5000 + +export async function refreshModelCapabilitiesOnStartup( + config: ModelCapabilitiesConfig | undefined, +): Promise { + if (config?.enabled === false) { + return + } + + if (config?.auto_refresh_on_start === false) { + return + } + + const timeoutMs = config?.refresh_timeout_ms ?? DEFAULT_REFRESH_TIMEOUT_MS + + let timeoutId: ReturnType | undefined + try { + await Promise.race([ + refreshModelCapabilitiesCache({ + sourceUrl: config?.source_url, + }), + new Promise((_, reject) => { + timeoutId = setTimeout(() => reject(new Error("Model capabilities refresh timed out")), timeoutMs) + }), + ]) + } catch (error) { + log("[auto-update-checker] Model capabilities refresh failed", { error: String(error) }) + } finally { + if (timeoutId) { + clearTimeout(timeoutId) + } + } +} diff --git a/src/hooks/auto-update-checker/types.ts b/src/hooks/auto-update-checker/types.ts index 550e5137f..460970f67 100644 --- a/src/hooks/auto-update-checker/types.ts +++ b/src/hooks/auto-update-checker/types.ts @@ -1,3 +1,5 @@ +import type { ModelCapabilitiesConfig } from "../../config/schema/model-capabilities" + export interface NpmDistTags { latest: string [key: string]: string @@ -26,4 +28,5 @@ export interface AutoUpdateCheckerOptions { showStartupToast?: boolean isSisyphusEnabled?: boolean autoUpdate?: boolean + modelCapabilities?: ModelCapabilitiesConfig } diff --git a/src/plugin/chat-params.test.ts b/src/plugin/chat-params.test.ts index c646c8283..511394a75 100644 --- a/src/plugin/chat-params.test.ts +++ b/src/plugin/chat-params.test.ts @@ -113,7 +113,6 @@ describe("createChatParamsHandler", () => { //#then expect(output).toEqual({ - temperature: 0.4, topP: 0.7, topK: 1, options: { @@ -133,4 +132,86 @@ describe("createChatParamsHandler", () => { }, }) }) + + test("drops unsupported temperature and clamps maxTokens from bundled model capabilities", async () => { + //#given + setSessionPromptParams("ses_chat_params", { + temperature: 0.7, + options: { + maxTokens: 200_000, + }, + }) + + const handler = createChatParamsHandler({ + anthropicEffort: null, + }) + + const input = { + sessionID: "ses_chat_params", + agent: { name: "oracle" }, + model: { providerID: "openai", modelID: "gpt-5.4" }, + provider: { id: "openai" }, + message: {}, + } + + const output = { + temperature: 0.1, + topP: 1, + topK: 1, + options: {}, + } + + //#when + await handler(input, output) + + //#then + expect(output).toEqual({ + topP: 1, + topK: 1, + options: { + maxTokens: 128_000, + }, + }) + }) + + test("drops unsupported reasoning settings from bundled model capabilities", async () => { + //#given + setSessionPromptParams("ses_chat_params", { + temperature: 0.4, + options: { + reasoningEffort: "high", + thinking: { type: "enabled", budgetTokens: 4096 }, + }, + }) + + const handler = createChatParamsHandler({ + anthropicEffort: null, + }) + + const input = { + sessionID: "ses_chat_params", + agent: { name: "oracle" }, + model: { providerID: "openai", modelID: "gpt-4.1" }, + provider: { id: "openai" }, + message: {}, + } + + const output = { + temperature: 0.1, + topP: 1, + topK: 1, + options: {}, + } + + //#when + await handler(input, output) + + //#then + expect(output).toEqual({ + temperature: 0.4, + topP: 1, + topK: 1, + options: {}, + }) + }) }) diff --git a/src/plugin/chat-params.ts b/src/plugin/chat-params.ts index d265b57d3..d69a14f8e 100644 --- a/src/plugin/chat-params.ts +++ b/src/plugin/chat-params.ts @@ -1,6 +1,6 @@ import { normalizeSDKResponse } from "../shared/normalize-sdk-response" import { getSessionPromptParams } from "../shared/session-prompt-params-state" -import { resolveCompatibleModelSettings } from "../shared" +import { getModelCapabilities, resolveCompatibleModelSettings } from "../shared" export type ChatParamsInput = { sessionID: string @@ -21,25 +21,6 @@ export type ChatParamsOutput = { options: Record } -type ProviderListClient = { - provider?: { - list?: () => Promise - } -} - -type ProviderModelMetadata = { - variants?: Record -} - -type ProviderListEntry = { - id?: string - models?: Record -} - -type ProviderListData = { - all?: ProviderListEntry[] -} - function isRecord(value: unknown): value is Record { return typeof value === "object" && value !== null } @@ -101,33 +82,9 @@ function isChatParamsOutput(raw: unknown): raw is ChatParamsOutput { return isRecord(raw.options) } -async function getVariantCapabilities( - client: ProviderListClient | undefined, - model: { providerID: string; modelID: string }, -): Promise { - const providerList = client?.provider?.list - if (typeof providerList !== "function") { - return undefined - } - - try { - const response = await providerList() - const data = normalizeSDKResponse(response, {}) - const providerEntry = data.all?.find((entry) => entry.id === model.providerID) - const variants = providerEntry?.models?.[model.modelID]?.variants - if (!variants) { - return undefined - } - - return Object.keys(variants) - } catch { - return undefined - } -} - export function createChatParamsHandler(args: { anthropicEffort: { "chat.params"?: (input: ChatParamsHookInput, output: ChatParamsOutput) => Promise } | null - client?: ProviderListClient + client?: unknown }): (input: unknown, output: unknown) => Promise { return async (input, output): Promise => { const normalizedInput = buildChatParamsInput(input) @@ -150,7 +107,10 @@ export function createChatParamsHandler(args: { } } - const variantCapabilities = await getVariantCapabilities(args.client, normalizedInput.model) + const capabilities = getModelCapabilities({ + providerID: normalizedInput.model.providerID, + modelID: normalizedInput.model.modelID, + }) const compatibility = resolveCompatibleModelSettings({ providerID: normalizedInput.model.providerID, @@ -162,10 +122,12 @@ export function createChatParamsHandler(args: { reasoningEffort: typeof output.options.reasoningEffort === "string" ? output.options.reasoningEffort : undefined, + temperature: typeof output.temperature === "number" ? output.temperature : undefined, + topP: typeof output.topP === "number" ? output.topP : undefined, + maxTokens: typeof output.options.maxTokens === "number" ? output.options.maxTokens : undefined, + thinking: isRecord(output.options.thinking) ? output.options.thinking : undefined, }, - capabilities: { - variants: variantCapabilities, - }, + capabilities, }) if (normalizedInput.rawMessage) { @@ -183,6 +145,38 @@ export function createChatParamsHandler(args: { delete output.options.reasoningEffort } + if ("temperature" in compatibility) { + if (compatibility.temperature !== undefined) { + output.temperature = compatibility.temperature + } else { + delete output.temperature + } + } + + if ("topP" in compatibility) { + if (compatibility.topP !== undefined) { + output.topP = compatibility.topP + } else { + delete output.topP + } + } + + if ("maxTokens" in compatibility) { + if (compatibility.maxTokens !== undefined) { + output.options.maxTokens = compatibility.maxTokens + } else { + delete output.options.maxTokens + } + } + + if ("thinking" in compatibility) { + if (compatibility.thinking !== undefined) { + output.options.thinking = compatibility.thinking + } else { + delete output.options.thinking + } + } + await args.anthropicEffort?.["chat.params"]?.(normalizedInput, output) } } diff --git a/src/plugin/hooks/create-session-hooks.ts b/src/plugin/hooks/create-session-hooks.ts index daa5e4ff5..60ea82415 100644 --- a/src/plugin/hooks/create-session-hooks.ts +++ b/src/plugin/hooks/create-session-hooks.ts @@ -184,6 +184,7 @@ export function createSessionHooks(args: { showStartupToast: isHookEnabled("startup-toast"), isSisyphusEnabled: pluginConfig.sisyphus_agent?.disabled !== true, autoUpdate: pluginConfig.auto_update ?? true, + modelCapabilities: pluginConfig.model_capabilities, })) : null diff --git a/src/shared/connected-providers-cache.test.ts b/src/shared/connected-providers-cache.test.ts index 183f9c712..cd2573b93 100644 --- a/src/shared/connected-providers-cache.test.ts +++ b/src/shared/connected-providers-cache.test.ts @@ -7,6 +7,7 @@ import { tmpdir } from "node:os" import { join } from "node:path" import { createConnectedProvidersCacheStore, + findProviderModelMetadata, } from "./connected-providers-cache" let fakeUserCacheRoot = "" @@ -68,8 +69,14 @@ describe("updateConnectedProvidersCache", () => { expect(cache).not.toBeNull() expect(cache!.connected).toEqual(["openai", "anthropic"]) expect(cache!.models).toEqual({ - openai: ["gpt-5.3-codex", "gpt-5.4"], - anthropic: ["claude-opus-4-6", "claude-sonnet-4-6"], + openai: [ + { id: "gpt-5.3-codex", name: "GPT-5.3 Codex" }, + { id: "gpt-5.4", name: "GPT-5.4" }, + ], + anthropic: [ + { id: "claude-opus-4-6", name: "Claude Opus 4.6" }, + { id: "claude-sonnet-4-6", name: "Claude Sonnet 4.6" }, + ], }) }) @@ -174,4 +181,52 @@ describe("updateConnectedProvidersCache", () => { } } }) + + test("findProviderModelMetadata returns rich cached metadata", async () => { + //#given + const mockClient = { + provider: { + list: async () => ({ + data: { + connected: ["openai"], + all: [ + { + id: "openai", + models: { + "gpt-5.4": { + id: "gpt-5.4", + name: "GPT-5.4", + temperature: false, + variants: { + low: {}, + high: {}, + }, + limit: { output: 128000 }, + }, + }, + }, + ], + }, + }), + }, + } + + await testCacheStore.updateConnectedProvidersCache(mockClient) + const cache = testCacheStore.readProviderModelsCache() + + //#when + const result = findProviderModelMetadata("openai", "gpt-5.4", cache) + + //#then + expect(result).toEqual({ + id: "gpt-5.4", + name: "GPT-5.4", + temperature: false, + variants: { + low: {}, + high: {}, + }, + limit: { output: 128000 }, + }) + }) }) diff --git a/src/shared/connected-providers-cache.ts b/src/shared/connected-providers-cache.ts index 692d35dd2..61003cd38 100644 --- a/src/shared/connected-providers-cache.ts +++ b/src/shared/connected-providers-cache.ts @@ -11,20 +11,39 @@ interface ConnectedProvidersCache { updatedAt: string } -interface ModelMetadata { +export interface ModelMetadata { id: string provider?: string context?: number output?: number name?: string + variants?: Record + limit?: { + context?: number + input?: number + output?: number + } + modalities?: { + input?: string[] + output?: string[] + } + capabilities?: Record + reasoning?: boolean + temperature?: boolean + tool_call?: boolean + [key: string]: unknown } -interface ProviderModelsCache { +export interface ProviderModelsCache { models: Record connected: string[] updatedAt: string } +function isRecord(value: unknown): value is Record { + return typeof value === "object" && value !== null +} + export function createConnectedProvidersCacheStore( getCacheDir: () => string = dataPath.getOmoOpenCodeCacheDir ) { @@ -119,7 +138,7 @@ export function createConnectedProvidersCacheStore( return existsSync(cacheFile) } - function writeProviderModelsCache(data: { models: Record; connected: string[] }): void { + function writeProviderModelsCache(data: { models: Record; connected: string[] }): void { ensureCacheDir() const cacheFile = getCacheFilePath(PROVIDER_MODELS_CACHE_FILE) @@ -164,14 +183,27 @@ export function createConnectedProvidersCacheStore( writeConnectedProvidersCache(connected) - const modelsByProvider: Record = {} + const modelsByProvider: Record = {} const allProviders = result.data?.all ?? [] for (const provider of allProviders) { if (provider.models) { - const modelIds = Object.keys(provider.models) - if (modelIds.length > 0) { - modelsByProvider[provider.id] = modelIds + const modelMetadata = Object.entries(provider.models).map(([modelID, rawMetadata]) => { + if (!isRecord(rawMetadata)) { + return { id: modelID } + } + + const normalizedID = typeof rawMetadata.id === "string" + ? rawMetadata.id + : modelID + + return { + id: normalizedID, + ...rawMetadata, + } satisfies ModelMetadata + }) + if (modelMetadata.length > 0) { + modelsByProvider[provider.id] = modelMetadata } } } @@ -200,6 +232,32 @@ export function createConnectedProvidersCacheStore( } } +export function findProviderModelMetadata( + providerID: string, + modelID: string, + cache: ProviderModelsCache | null = defaultConnectedProvidersCacheStore.readProviderModelsCache(), +): ModelMetadata | undefined { + const providerModels = cache?.models?.[providerID] + if (!providerModels) { + return undefined + } + + for (const entry of providerModels) { + if (typeof entry === "string") { + if (entry === modelID) { + return { id: entry } + } + continue + } + + if (entry?.id === modelID) { + return entry + } + } + + return undefined +} + const defaultConnectedProvidersCacheStore = createConnectedProvidersCacheStore( () => dataPath.getOmoOpenCodeCacheDir() ) diff --git a/src/shared/index.ts b/src/shared/index.ts index 9f296d797..726b55fa5 100644 --- a/src/shared/index.ts +++ b/src/shared/index.ts @@ -43,6 +43,9 @@ export type { ModelResolutionResult, } from "./model-resolution-types" export * from "./model-availability" +export * from "./model-capabilities" +export * from "./model-capabilities-cache" +export * from "./model-capability-heuristics" export * from "./model-settings-compatibility" export * from "./fallback-model-availability" export * from "./connected-providers-cache" diff --git a/src/shared/model-capabilities-cache.test.ts b/src/shared/model-capabilities-cache.test.ts new file mode 100644 index 000000000..2575577c3 --- /dev/null +++ b/src/shared/model-capabilities-cache.test.ts @@ -0,0 +1,134 @@ +/// + +import { afterEach, beforeEach, describe, expect, test } from "bun:test" + +import { existsSync, mkdirSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from "node:fs" +import { tmpdir } from "node:os" +import { join } from "node:path" +import { + buildModelCapabilitiesSnapshotFromModelsDev, + createModelCapabilitiesCacheStore, + MODELS_DEV_SOURCE_URL, +} from "./model-capabilities-cache" + +let fakeUserCacheRoot = "" +let testCacheDir = "" + +describe("model-capabilities-cache", () => { + beforeEach(() => { + fakeUserCacheRoot = mkdtempSync(join(tmpdir(), "model-capabilities-cache-")) + testCacheDir = join(fakeUserCacheRoot, "oh-my-opencode") + }) + + afterEach(() => { + if (existsSync(fakeUserCacheRoot)) { + rmSync(fakeUserCacheRoot, { recursive: true, force: true }) + } + fakeUserCacheRoot = "" + testCacheDir = "" + }) + + test("builds a normalized snapshot from provider-keyed models.dev data", () => { + //#given + const raw = { + openai: { + models: { + "gpt-5.4": { + id: "gpt-5.4", + family: "gpt", + reasoning: true, + temperature: false, + tool_call: true, + modalities: { + input: ["text", "image"], + output: ["text"], + }, + limit: { + context: 1_050_000, + output: 128_000, + }, + }, + }, + }, + anthropic: { + models: { + "claude-sonnet-4-6": { + family: "claude-sonnet", + reasoning: true, + temperature: true, + limit: { + context: 1_000_000, + output: 64_000, + }, + }, + }, + }, + } + + //#when + const snapshot = buildModelCapabilitiesSnapshotFromModelsDev(raw) + + //#then + expect(snapshot.sourceUrl).toBe(MODELS_DEV_SOURCE_URL) + expect(snapshot.models["gpt-5.4"]).toEqual({ + id: "gpt-5.4", + family: "gpt", + reasoning: true, + temperature: false, + toolCall: true, + modalities: { + input: ["text", "image"], + output: ["text"], + }, + limit: { + context: 1_050_000, + output: 128_000, + }, + }) + expect(snapshot.models["claude-sonnet-4-6"]).toEqual({ + id: "claude-sonnet-4-6", + family: "claude-sonnet", + reasoning: true, + temperature: true, + limit: { + context: 1_000_000, + output: 64_000, + }, + }) + }) + + test("refresh writes cache and preserves unrelated files in the cache directory", async () => { + //#given + const sentinelPath = join(testCacheDir, "keep-me.json") + const store = createModelCapabilitiesCacheStore(() => testCacheDir) + mkdirSync(testCacheDir, { recursive: true }) + writeFileSync(sentinelPath, JSON.stringify({ keep: true })) + + const fetchImpl: typeof fetch = async () => + new Response(JSON.stringify({ + openai: { + models: { + "gpt-5.4": { + id: "gpt-5.4", + family: "gpt", + reasoning: true, + limit: { output: 128_000 }, + }, + }, + }, + }), { + status: 200, + headers: { "content-type": "application/json" }, + }) + + //#when + const snapshot = await store.refreshModelCapabilitiesCache({ fetchImpl }) + const reloadedStore = createModelCapabilitiesCacheStore(() => testCacheDir) + + //#then + expect(snapshot.models["gpt-5.4"]?.limit?.output).toBe(128_000) + expect(existsSync(sentinelPath)).toBe(true) + expect(readFileSync(sentinelPath, "utf-8")).toBe(JSON.stringify({ keep: true })) + expect(reloadedStore.readModelCapabilitiesCache()).toEqual(snapshot) + }) +}) diff --git a/src/shared/model-capabilities-cache.ts b/src/shared/model-capabilities-cache.ts new file mode 100644 index 000000000..c3339cd8d --- /dev/null +++ b/src/shared/model-capabilities-cache.ts @@ -0,0 +1,241 @@ +import { existsSync, mkdirSync, readFileSync, writeFileSync } from "fs" +import { join } from "path" +import * as dataPath from "./data-path" +import { log } from "./logger" +import type { ModelCapabilitiesSnapshot, ModelCapabilitiesSnapshotEntry } from "./model-capabilities" + +export const MODELS_DEV_SOURCE_URL = "https://models.dev/api.json" +const MODEL_CAPABILITIES_CACHE_FILE = "model-capabilities.json" + +function isRecord(value: unknown): value is Record { + return typeof value === "object" && value !== null +} + +function readBoolean(value: unknown): boolean | undefined { + return typeof value === "boolean" ? value : undefined +} + +function readNumber(value: unknown): number | undefined { + return typeof value === "number" ? value : undefined +} + +function readString(value: unknown): string | undefined { + return typeof value === "string" ? value : undefined +} + +function readStringArray(value: unknown): string[] | undefined { + if (!Array.isArray(value)) { + return undefined + } + + const result = value.filter((item): item is string => typeof item === "string") + return result.length > 0 ? result : undefined +} + +function normalizeSnapshotEntry(rawModelID: string, rawModel: unknown): ModelCapabilitiesSnapshotEntry | undefined { + if (!isRecord(rawModel)) { + return undefined + } + + const id = readString(rawModel.id) ?? rawModelID + const family = readString(rawModel.family) + const reasoning = readBoolean(rawModel.reasoning) + const temperature = readBoolean(rawModel.temperature) + const toolCall = readBoolean(rawModel.tool_call) + + const rawModalities = isRecord(rawModel.modalities) ? rawModel.modalities : undefined + const modalitiesInput = readStringArray(rawModalities?.input) + const modalitiesOutput = readStringArray(rawModalities?.output) + const modalities = modalitiesInput || modalitiesOutput + ? { + ...(modalitiesInput ? { input: modalitiesInput } : {}), + ...(modalitiesOutput ? { output: modalitiesOutput } : {}), + } + : undefined + + const rawLimit = isRecord(rawModel.limit) ? rawModel.limit : undefined + const limitContext = readNumber(rawLimit?.context) + const limitInput = readNumber(rawLimit?.input) + const limitOutput = readNumber(rawLimit?.output) + const limit = limitContext !== undefined || limitInput !== undefined || limitOutput !== undefined + ? { + ...(limitContext !== undefined ? { context: limitContext } : {}), + ...(limitInput !== undefined ? { input: limitInput } : {}), + ...(limitOutput !== undefined ? { output: limitOutput } : {}), + } + : undefined + + return { + id, + ...(family ? { family } : {}), + ...(reasoning !== undefined ? { reasoning } : {}), + ...(temperature !== undefined ? { temperature } : {}), + ...(toolCall !== undefined ? { toolCall } : {}), + ...(modalities ? { modalities } : {}), + ...(limit ? { limit } : {}), + } +} + +function mergeSnapshotEntries( + existing: ModelCapabilitiesSnapshotEntry | undefined, + incoming: ModelCapabilitiesSnapshotEntry, +): ModelCapabilitiesSnapshotEntry { + if (!existing) { + return incoming + } + + return { + ...existing, + ...incoming, + modalities: { + ...existing.modalities, + ...incoming.modalities, + }, + limit: { + ...existing.limit, + ...incoming.limit, + }, + } +} + +export function buildModelCapabilitiesSnapshotFromModelsDev(raw: unknown): ModelCapabilitiesSnapshot { + const models: Record = {} + const providers = isRecord(raw) ? raw : {} + + for (const providerValue of Object.values(providers)) { + if (!isRecord(providerValue)) { + continue + } + + const providerModels = providerValue.models + if (!isRecord(providerModels)) { + continue + } + + for (const [rawModelID, rawModel] of Object.entries(providerModels)) { + const normalizedEntry = normalizeSnapshotEntry(rawModelID, rawModel) + if (!normalizedEntry) { + continue + } + + models[normalizedEntry.id.toLowerCase()] = mergeSnapshotEntries( + models[normalizedEntry.id.toLowerCase()], + normalizedEntry, + ) + } + } + + return { + generatedAt: new Date().toISOString(), + sourceUrl: MODELS_DEV_SOURCE_URL, + models, + } +} + +export async function fetchModelCapabilitiesSnapshot(args: { + sourceUrl?: string + fetchImpl?: typeof fetch +} = {}): Promise { + const sourceUrl = args.sourceUrl ?? MODELS_DEV_SOURCE_URL + const fetchImpl = args.fetchImpl ?? fetch + const response = await fetchImpl(sourceUrl) + + if (!response.ok) { + throw new Error(`models.dev fetch failed with ${response.status}`) + } + + const raw = await response.json() + const snapshot = buildModelCapabilitiesSnapshotFromModelsDev(raw) + return { + ...snapshot, + sourceUrl, + } +} + +export function createModelCapabilitiesCacheStore( + getCacheDir: () => string = dataPath.getOmoOpenCodeCacheDir, +) { + let memSnapshot: ModelCapabilitiesSnapshot | null | undefined + + function getCacheFilePath(): string { + return join(getCacheDir(), MODEL_CAPABILITIES_CACHE_FILE) + } + + function ensureCacheDir(): void { + const cacheDir = getCacheDir() + if (!existsSync(cacheDir)) { + mkdirSync(cacheDir, { recursive: true }) + } + } + + function readModelCapabilitiesCache(): ModelCapabilitiesSnapshot | null { + if (memSnapshot !== undefined) { + return memSnapshot + } + + const cacheFile = getCacheFilePath() + if (!existsSync(cacheFile)) { + memSnapshot = null + log("[model-capabilities-cache] Cache file not found", { cacheFile }) + return null + } + + try { + const content = readFileSync(cacheFile, "utf-8") + const snapshot = JSON.parse(content) as ModelCapabilitiesSnapshot + memSnapshot = snapshot + log("[model-capabilities-cache] Read cache", { + modelCount: Object.keys(snapshot.models).length, + generatedAt: snapshot.generatedAt, + }) + return snapshot + } catch (error) { + memSnapshot = null + log("[model-capabilities-cache] Error reading cache", { error: String(error) }) + return null + } + } + + function hasModelCapabilitiesCache(): boolean { + return existsSync(getCacheFilePath()) + } + + function writeModelCapabilitiesCache(snapshot: ModelCapabilitiesSnapshot): void { + ensureCacheDir() + const cacheFile = getCacheFilePath() + + writeFileSync(cacheFile, JSON.stringify(snapshot, null, 2) + "\n") + memSnapshot = snapshot + log("[model-capabilities-cache] Cache written", { + modelCount: Object.keys(snapshot.models).length, + generatedAt: snapshot.generatedAt, + }) + } + + async function refreshModelCapabilitiesCache(args: { + sourceUrl?: string + fetchImpl?: typeof fetch + } = {}): Promise { + const snapshot = await fetchModelCapabilitiesSnapshot(args) + writeModelCapabilitiesCache(snapshot) + return snapshot + } + + return { + readModelCapabilitiesCache, + hasModelCapabilitiesCache, + writeModelCapabilitiesCache, + refreshModelCapabilitiesCache, + } +} + +const defaultModelCapabilitiesCacheStore = createModelCapabilitiesCacheStore( + () => dataPath.getOmoOpenCodeCacheDir(), +) + +export const { + readModelCapabilitiesCache, + hasModelCapabilitiesCache, + writeModelCapabilitiesCache, + refreshModelCapabilitiesCache, +} = defaultModelCapabilitiesCacheStore diff --git a/src/shared/model-capabilities.test.ts b/src/shared/model-capabilities.test.ts new file mode 100644 index 000000000..172e7a523 --- /dev/null +++ b/src/shared/model-capabilities.test.ts @@ -0,0 +1,159 @@ +import { describe, expect, test } from "bun:test" + +import { + getModelCapabilities, + type ModelCapabilitiesSnapshot, +} from "./model-capabilities" + +describe("getModelCapabilities", () => { + const bundledSnapshot: ModelCapabilitiesSnapshot = { + generatedAt: "2026-03-25T00:00:00.000Z", + sourceUrl: "https://models.dev/api.json", + models: { + "claude-opus-4-6": { + id: "claude-opus-4-6", + family: "claude-opus", + reasoning: true, + temperature: true, + modalities: { + input: ["text", "image", "pdf"], + output: ["text"], + }, + limit: { + context: 1_000_000, + output: 128_000, + }, + toolCall: true, + }, + "gemini-3.1-pro-preview": { + id: "gemini-3.1-pro-preview", + family: "gemini", + reasoning: true, + temperature: true, + modalities: { + input: ["text", "image"], + output: ["text"], + }, + limit: { + context: 1_000_000, + output: 65_000, + }, + }, + "gpt-5.4": { + id: "gpt-5.4", + family: "gpt", + reasoning: true, + temperature: false, + modalities: { + input: ["text", "image", "pdf"], + output: ["text"], + }, + limit: { + context: 1_050_000, + output: 128_000, + }, + }, + }, + } + + test("uses runtime metadata before snapshot data", () => { + const result = getModelCapabilities({ + providerID: "anthropic", + modelID: "claude-opus-4-6", + runtimeModel: { + variants: { + low: {}, + medium: {}, + high: {}, + }, + }, + bundledSnapshot, + }) + + expect(result).toMatchObject({ + canonicalModelID: "claude-opus-4-6", + family: "claude-opus", + variants: ["low", "medium", "high"], + supportsThinking: true, + supportsTemperature: true, + maxOutputTokens: 128_000, + toolCall: true, + }) + }) + + test("normalizes thinking suffix aliases before snapshot lookup", () => { + const result = getModelCapabilities({ + providerID: "anthropic", + modelID: "claude-opus-4-6-thinking", + bundledSnapshot, + }) + + expect(result).toMatchObject({ + canonicalModelID: "claude-opus-4-6", + family: "claude-opus", + supportsThinking: true, + supportsTemperature: true, + maxOutputTokens: 128_000, + }) + }) + + test("maps local gemini aliases to canonical models.dev entries", () => { + const result = getModelCapabilities({ + providerID: "google", + modelID: "gemini-3.1-pro-high", + bundledSnapshot, + }) + + expect(result).toMatchObject({ + canonicalModelID: "gemini-3.1-pro-preview", + family: "gemini", + supportsThinking: true, + supportsTemperature: true, + maxOutputTokens: 65_000, + }) + }) + + test("prefers runtime models.dev cache over bundled snapshot", () => { + const runtimeSnapshot: ModelCapabilitiesSnapshot = { + ...bundledSnapshot, + models: { + ...bundledSnapshot.models, + "gpt-5.4": { + ...bundledSnapshot.models["gpt-5.4"], + limit: { + context: 1_050_000, + output: 64_000, + }, + }, + }, + } + + const result = getModelCapabilities({ + providerID: "openai", + modelID: "gpt-5.4", + bundledSnapshot, + runtimeSnapshot, + }) + + expect(result).toMatchObject({ + canonicalModelID: "gpt-5.4", + maxOutputTokens: 64_000, + supportsTemperature: false, + }) + }) + + test("falls back to heuristic family rules when no snapshot entry exists", () => { + const result = getModelCapabilities({ + providerID: "openai", + modelID: "o3-mini", + bundledSnapshot, + }) + + expect(result).toMatchObject({ + canonicalModelID: "o3-mini", + family: "openai-reasoning", + variants: ["low", "medium", "high"], + reasoningEfforts: ["none", "minimal", "low", "medium", "high"], + }) + }) +}) diff --git a/src/shared/model-capabilities.ts b/src/shared/model-capabilities.ts new file mode 100644 index 000000000..887d15286 --- /dev/null +++ b/src/shared/model-capabilities.ts @@ -0,0 +1,228 @@ +import bundledModelCapabilitiesSnapshotJson from "../generated/model-capabilities.generated.json" +import { findProviderModelMetadata, type ModelMetadata } from "./connected-providers-cache" +import { detectHeuristicModelFamily } from "./model-capability-heuristics" + +export type ModelCapabilitiesSnapshotEntry = { + id: string + family?: string + reasoning?: boolean + temperature?: boolean + toolCall?: boolean + modalities?: { + input?: string[] + output?: string[] + } + limit?: { + context?: number + input?: number + output?: number + } +} + +export type ModelCapabilitiesSnapshot = { + generatedAt: string + sourceUrl: string + models: Record +} + +export type ModelCapabilities = { + requestedModelID: string + canonicalModelID: string + family?: string + variants?: string[] + reasoningEfforts?: string[] + reasoning?: boolean + supportsThinking?: boolean + supportsTemperature?: boolean + supportsTopP?: boolean + maxOutputTokens?: number + toolCall?: boolean + modalities?: { + input?: string[] + output?: string[] + } +} + +type GetModelCapabilitiesInput = { + providerID: string + modelID: string + runtimeModel?: ModelMetadata | Record + runtimeSnapshot?: ModelCapabilitiesSnapshot + bundledSnapshot?: ModelCapabilitiesSnapshot +} + +type ModelCapabilityOverride = { + canonicalModelID?: string + variants?: string[] + reasoningEfforts?: string[] + supportsThinking?: boolean + supportsTemperature?: boolean + supportsTopP?: boolean +} + +const MODEL_ID_OVERRIDES: Record = { + "claude-opus-4-6-thinking": { canonicalModelID: "claude-opus-4-6" }, + "claude-sonnet-4-6-thinking": { canonicalModelID: "claude-sonnet-4-6" }, + "claude-opus-4-5-thinking": { canonicalModelID: "claude-opus-4-5-20251101" }, + "gpt-5.3-codex-spark": { canonicalModelID: "gpt-5.3-codex" }, + "gemini-3.1-pro-high": { canonicalModelID: "gemini-3.1-pro-preview" }, + "gemini-3.1-pro-low": { canonicalModelID: "gemini-3.1-pro-preview" }, + "gemini-3-pro-high": { canonicalModelID: "gemini-3-pro-preview" }, + "gemini-3-pro-low": { canonicalModelID: "gemini-3-pro-preview" }, +} + +function isRecord(value: unknown): value is Record { + return typeof value === "object" && value !== null +} + +function normalizeLookupModelID(modelID: string): string { + return modelID.trim().toLowerCase() +} + +function readBoolean(value: unknown): boolean | undefined { + return typeof value === "boolean" ? value : undefined +} + +function readNumber(value: unknown): number | undefined { + return typeof value === "number" ? value : undefined +} + +function readStringArray(value: unknown): string[] | undefined { + if (!Array.isArray(value)) { + return undefined + } + + const strings = value.filter((item): item is string => typeof item === "string") + return strings.length > 0 ? strings : undefined +} + +function normalizeVariantKeys(value: unknown): string[] | undefined { + if (!isRecord(value)) { + return undefined + } + + const variants = Object.keys(value).map((variant) => variant.toLowerCase()) + return variants.length > 0 ? variants : undefined +} + +function normalizeModalities(value: unknown): ModelCapabilities["modalities"] | undefined { + if (!isRecord(value)) { + return undefined + } + + const input = readStringArray(value.input) + const output = readStringArray(value.output) + + if (!input && !output) { + return undefined + } + + return { + ...(input ? { input } : {}), + ...(output ? { output } : {}), + } +} + +function normalizeSnapshot(snapshot: ModelCapabilitiesSnapshot | typeof bundledModelCapabilitiesSnapshotJson): ModelCapabilitiesSnapshot { + return snapshot as ModelCapabilitiesSnapshot +} + +function getCanonicalModelID(modelID: string): string { + const normalizedModelID = normalizeLookupModelID(modelID) + const override = MODEL_ID_OVERRIDES[normalizedModelID] + if (override?.canonicalModelID) { + return override.canonicalModelID + } + + if (normalizedModelID.startsWith("claude-") && normalizedModelID.endsWith("-thinking")) { + return normalizedModelID.replace(/-thinking$/i, "") + } + + return normalizedModelID +} + +function getOverride(modelID: string): ModelCapabilityOverride | undefined { + return MODEL_ID_OVERRIDES[normalizeLookupModelID(modelID)] +} + +function readRuntimeModelLimitOutput(runtimeModel: Record | undefined): number | undefined { + if (!runtimeModel) { + return undefined + } + + const limit = runtimeModel.limit + if (!isRecord(limit)) { + return undefined + } + + return readNumber(limit.output) +} + +function readRuntimeModelBoolean(runtimeModel: Record | undefined, keys: string[]): boolean | undefined { + if (!runtimeModel) { + return undefined + } + + for (const key of keys) { + const value = runtimeModel[key] + if (typeof value === "boolean") { + return value + } + } + + return undefined +} + +function readRuntimeModel(runtimeModel: ModelMetadata | Record | undefined): Record | undefined { + return isRecord(runtimeModel) ? runtimeModel : undefined +} + +const bundledModelCapabilitiesSnapshot = normalizeSnapshot(bundledModelCapabilitiesSnapshotJson) + +export function getBundledModelCapabilitiesSnapshot(): ModelCapabilitiesSnapshot { + return bundledModelCapabilitiesSnapshot +} + +export function getModelCapabilities(input: GetModelCapabilitiesInput): ModelCapabilities { + const requestedModelID = normalizeLookupModelID(input.modelID) + const canonicalModelID = getCanonicalModelID(input.modelID) + const override = getOverride(input.modelID) + const runtimeModel = readRuntimeModel( + input.runtimeModel ?? findProviderModelMetadata(input.providerID, input.modelID), + ) + const runtimeSnapshot = input.runtimeSnapshot + const bundledSnapshot = input.bundledSnapshot ?? bundledModelCapabilitiesSnapshot + const snapshotEntry = runtimeSnapshot?.models?.[canonicalModelID] ?? bundledSnapshot.models[canonicalModelID] + const heuristicFamily = detectHeuristicModelFamily(canonicalModelID) + const runtimeVariants = normalizeVariantKeys(runtimeModel?.variants) + + return { + requestedModelID, + canonicalModelID, + family: snapshotEntry?.family ?? heuristicFamily?.family, + variants: runtimeVariants ?? override?.variants ?? heuristicFamily?.variants, + reasoningEfforts: override?.reasoningEfforts ?? heuristicFamily?.reasoningEfforts, + reasoning: readRuntimeModelBoolean(runtimeModel, ["reasoning"]) ?? snapshotEntry?.reasoning, + supportsThinking: + override?.supportsThinking + ?? heuristicFamily?.supportsThinking + ?? readRuntimeModelBoolean(runtimeModel, ["reasoning"]) + ?? snapshotEntry?.reasoning, + supportsTemperature: + readRuntimeModelBoolean(runtimeModel, ["temperature"]) + ?? override?.supportsTemperature + ?? snapshotEntry?.temperature, + supportsTopP: + readRuntimeModelBoolean(runtimeModel, ["topP", "top_p"]) + ?? override?.supportsTopP, + maxOutputTokens: + readRuntimeModelLimitOutput(runtimeModel) + ?? snapshotEntry?.limit?.output, + toolCall: + readRuntimeModelBoolean(runtimeModel, ["toolCall", "tool_call"]) + ?? snapshotEntry?.toolCall, + modalities: + normalizeModalities(runtimeModel?.modalities) + ?? snapshotEntry?.modalities, + } +} diff --git a/src/shared/model-capability-heuristics.ts b/src/shared/model-capability-heuristics.ts new file mode 100644 index 000000000..73286badc --- /dev/null +++ b/src/shared/model-capability-heuristics.ts @@ -0,0 +1,93 @@ +import { normalizeModelID } from "./model-normalization" + +export type HeuristicModelFamilyDefinition = { + family: string + includes?: string[] + pattern?: RegExp + variants?: string[] + reasoningEfforts?: string[] + supportsThinking?: boolean +} + +export const HEURISTIC_MODEL_FAMILY_REGISTRY: ReadonlyArray = [ + { + family: "claude-opus", + pattern: /claude(?:-\d+(?:-\d+)*)?-opus/, + variants: ["low", "medium", "high", "max"], + supportsThinking: true, + }, + { + family: "claude-non-opus", + includes: ["claude"], + variants: ["low", "medium", "high"], + supportsThinking: true, + }, + { + family: "openai-reasoning", + pattern: /^o\d(?:$|-)/, + variants: ["low", "medium", "high"], + reasoningEfforts: ["none", "minimal", "low", "medium", "high"], + }, + { + family: "gpt-5", + includes: ["gpt-5"], + variants: ["low", "medium", "high", "xhigh", "max"], + reasoningEfforts: ["none", "minimal", "low", "medium", "high", "xhigh"], + }, + { + family: "gpt-legacy", + includes: ["gpt"], + variants: ["low", "medium", "high"], + }, + { + family: "gemini", + includes: ["gemini"], + variants: ["low", "medium", "high"], + }, + { + family: "kimi", + includes: ["kimi", "k2"], + variants: ["low", "medium", "high"], + }, + { + family: "glm", + includes: ["glm"], + variants: ["low", "medium", "high"], + }, + { + family: "minimax", + includes: ["minimax"], + variants: ["low", "medium", "high"], + }, + { + family: "deepseek", + includes: ["deepseek"], + variants: ["low", "medium", "high"], + }, + { + family: "mistral", + includes: ["mistral", "codestral"], + variants: ["low", "medium", "high"], + }, + { + family: "llama", + includes: ["llama"], + variants: ["low", "medium", "high"], + }, +] + +export function detectHeuristicModelFamily(modelID: string): HeuristicModelFamilyDefinition | undefined { + const normalizedModelID = normalizeModelID(modelID).toLowerCase() + + for (const definition of HEURISTIC_MODEL_FAMILY_REGISTRY) { + if (definition.pattern?.test(normalizedModelID)) { + return definition + } + + if (definition.includes?.some((value) => normalizedModelID.includes(value))) { + return definition + } + } + + return undefined +} diff --git a/src/shared/model-settings-compatibility.test.ts b/src/shared/model-settings-compatibility.test.ts index 291da7547..fbb768981 100644 --- a/src/shared/model-settings-compatibility.test.ts +++ b/src/shared/model-settings-compatibility.test.ts @@ -418,6 +418,63 @@ describe("resolveCompatibleModelSettings", () => { ]) }) + test("drops unsupported temperature when capability metadata disables it", () => { + const result = resolveCompatibleModelSettings({ + providerID: "openai", + modelID: "gpt-5.4", + desired: { temperature: 0.7 }, + capabilities: { supportsTemperature: false }, + }) + + expect(result.temperature).toBeUndefined() + expect(result.changes).toEqual([ + { + field: "temperature", + from: "0.7", + to: undefined, + reason: "unsupported-by-model-metadata", + }, + ]) + }) + + test("drops thinking when model capabilities say it is unsupported", () => { + const result = resolveCompatibleModelSettings({ + providerID: "openai", + modelID: "gpt-5.4", + desired: { thinking: { type: "enabled", budgetTokens: 4096 } }, + capabilities: { supportsThinking: false }, + }) + + expect(result.thinking).toBeUndefined() + expect(result.changes).toEqual([ + { + field: "thinking", + from: "{\"type\":\"enabled\",\"budgetTokens\":4096}", + to: undefined, + reason: "unsupported-by-model-metadata", + }, + ]) + }) + + test("clamps maxTokens to the model output limit", () => { + const result = resolveCompatibleModelSettings({ + providerID: "openai", + modelID: "gpt-5.4", + desired: { maxTokens: 200_000 }, + capabilities: { maxOutputTokens: 128_000 }, + }) + + expect(result.maxTokens).toBe(128_000) + expect(result.changes).toEqual([ + { + field: "maxTokens", + from: "200000", + to: "128000", + reason: "max-output-limit", + }, + ]) + }) + // Passthrough: undefined desired values produce no changes test("no-op when desired settings are empty", () => { const result = resolveCompatibleModelSettings({ diff --git a/src/shared/model-settings-compatibility.ts b/src/shared/model-settings-compatibility.ts index 3ed4c7587..89661c2b2 100644 --- a/src/shared/model-settings-compatibility.ts +++ b/src/shared/model-settings-compatibility.ts @@ -1,84 +1,56 @@ -import { normalizeModelID } from "./model-normalization" +import { detectHeuristicModelFamily } from "./model-capability-heuristics" -type CompatibilityField = "variant" | "reasoningEffort" +type CompatibilityField = "variant" | "reasoningEffort" | "temperature" | "topP" | "maxTokens" | "thinking" type DesiredModelSettings = { variant?: string reasoningEffort?: string + temperature?: number + topP?: number + maxTokens?: number + thinking?: Record } -type VariantCapabilities = { +type CompatibilityCapabilities = { variants?: string[] + reasoningEfforts?: string[] + supportsTemperature?: boolean + supportsTopP?: boolean + maxOutputTokens?: number + supportsThinking?: boolean } export type ModelSettingsCompatibilityInput = { providerID: string modelID: string desired: DesiredModelSettings - capabilities?: VariantCapabilities + capabilities?: CompatibilityCapabilities } export type ModelSettingsCompatibilityChange = { field: CompatibilityField from: string to?: string - reason: "unsupported-by-model-family" | "unknown-model-family" | "unsupported-by-model-metadata" + reason: + | "unsupported-by-model-family" + | "unknown-model-family" + | "unsupported-by-model-metadata" + | "max-output-limit" } export type ModelSettingsCompatibilityResult = { variant?: string reasoningEffort?: string + temperature?: number + topP?: number + maxTokens?: number + thinking?: Record changes: ModelSettingsCompatibilityChange[] } -// --------------------------------------------------------------------------- -// Unified model family registry — detection rules + capabilities in ONE row. -// New model family = one entry. Zero code changes anywhere else. -// Order matters: more-specific patterns first (claude-opus before claude). -// --------------------------------------------------------------------------- - -type FamilyDefinition = { - /** Substring(s) in normalised model ID that identify this family (OR) */ - includes?: string[] - /** Regex when substring matching isn't enough */ - pattern?: RegExp - /** Supported variant levels (ordered low -> max) */ - variants: string[] - /** Supported reasoning-effort levels. Omit = not supported. */ - reasoningEffort?: string[] -} - -const MODEL_FAMILY_REGISTRY: ReadonlyArray = [ - ["claude-opus", { pattern: /claude(?:-\d+(?:-\d+)*)?-opus/, variants: ["low", "medium", "high", "max"] }], - ["claude-non-opus", { includes: ["claude"], variants: ["low", "medium", "high"] }], - ["openai-reasoning", { pattern: /^o\d(?:$|-)/, variants: ["low", "medium", "high"], reasoningEffort: ["none", "minimal", "low", "medium", "high"] }], - ["gpt-5", { includes: ["gpt-5"], variants: ["low", "medium", "high", "xhigh", "max"], reasoningEffort: ["none", "minimal", "low", "medium", "high", "xhigh"] }], - ["gpt-legacy", { includes: ["gpt"], variants: ["low", "medium", "high"] }], - ["gemini", { includes: ["gemini"], variants: ["low", "medium", "high"] }], - ["kimi", { includes: ["kimi", "k2"], variants: ["low", "medium", "high"] }], - ["glm", { includes: ["glm"], variants: ["low", "medium", "high"] }], - ["minimax", { includes: ["minimax"], variants: ["low", "medium", "high"] }], - ["deepseek", { includes: ["deepseek"], variants: ["low", "medium", "high"] }], - ["mistral", { includes: ["mistral", "codestral"], variants: ["low", "medium", "high"] }], - ["llama", { includes: ["llama"], variants: ["low", "medium", "high"] }], -] - const VARIANT_LADDER = ["low", "medium", "high", "xhigh", "max"] const REASONING_LADDER = ["none", "minimal", "low", "medium", "high", "xhigh"] -// --------------------------------------------------------------------------- -// Model family detection — single pass over the registry -// --------------------------------------------------------------------------- - -function detectFamily(_providerID: string, modelID: string): FamilyDefinition | undefined { - const model = normalizeModelID(modelID).toLowerCase() - for (const [, def] of MODEL_FAMILY_REGISTRY) { - if (def.pattern?.test(model)) return def - if (def.includes?.some((s) => model.includes(s))) return def - } - return undefined -} - // --------------------------------------------------------------------------- // Generic resolution — one function for both fields // --------------------------------------------------------------------------- @@ -96,13 +68,20 @@ function downgradeWithinLadder(value: string, allowed: string[], ladder: string[ return undefined } -function normalizeCapabilitiesVariants(capabilities: VariantCapabilities | undefined): string[] | undefined { +function normalizeCapabilitiesVariants(capabilities: CompatibilityCapabilities | undefined): string[] | undefined { if (!capabilities?.variants || capabilities.variants.length === 0) { return undefined } return capabilities.variants.map((v) => v.toLowerCase()) } +function normalizeCapabilitiesReasoningEfforts(capabilities: CompatibilityCapabilities | undefined): string[] | undefined { + if (!capabilities?.reasoningEfforts || capabilities.reasoningEfforts.length === 0) { + return undefined + } + return capabilities.reasoningEfforts.map((value) => value.toLowerCase()) +} + type FieldResolution = { value?: string; reason?: ModelSettingsCompatibilityChange["reason"] } function resolveField( @@ -146,10 +125,11 @@ function resolveField( export function resolveCompatibleModelSettings( input: ModelSettingsCompatibilityInput, ): ModelSettingsCompatibilityResult { - const family = detectFamily(input.providerID, input.modelID) + const family = detectHeuristicModelFamily(input.modelID) const familyKnown = family !== undefined const changes: ModelSettingsCompatibilityChange[] = [] const metadataVariants = normalizeCapabilitiesVariants(input.capabilities) + const metadataReasoningEfforts = normalizeCapabilitiesReasoningEfforts(input.capabilities) let variant = input.desired.variant if (variant !== undefined) { @@ -164,12 +144,68 @@ export function resolveCompatibleModelSettings( let reasoningEffort = input.desired.reasoningEffort if (reasoningEffort !== undefined) { const normalized = reasoningEffort.toLowerCase() - const resolved = resolveField(normalized, family?.reasoningEffort, REASONING_LADDER, familyKnown) + const resolved = resolveField(normalized, family?.reasoningEfforts, REASONING_LADDER, familyKnown, metadataReasoningEfforts) if (resolved.value !== normalized && resolved.reason) { changes.push({ field: "reasoningEffort", from: reasoningEffort, to: resolved.value, reason: resolved.reason }) } reasoningEffort = resolved.value } - return { variant, reasoningEffort, changes } + let temperature = input.desired.temperature + if (temperature !== undefined && input.capabilities?.supportsTemperature === false) { + changes.push({ + field: "temperature", + from: String(temperature), + to: undefined, + reason: "unsupported-by-model-metadata", + }) + temperature = undefined + } + + let topP = input.desired.topP + if (topP !== undefined && input.capabilities?.supportsTopP === false) { + changes.push({ + field: "topP", + from: String(topP), + to: undefined, + reason: "unsupported-by-model-metadata", + }) + topP = undefined + } + + let maxTokens = input.desired.maxTokens + if ( + maxTokens !== undefined && + input.capabilities?.maxOutputTokens !== undefined && + maxTokens > input.capabilities.maxOutputTokens + ) { + changes.push({ + field: "maxTokens", + from: String(maxTokens), + to: String(input.capabilities.maxOutputTokens), + reason: "max-output-limit", + }) + maxTokens = input.capabilities.maxOutputTokens + } + + let thinking = input.desired.thinking + if (thinking !== undefined && input.capabilities?.supportsThinking === false) { + changes.push({ + field: "thinking", + from: JSON.stringify(thinking), + to: undefined, + reason: "unsupported-by-model-metadata", + }) + thinking = undefined + } + + return { + variant, + reasoningEffort, + ...(input.desired.temperature !== undefined ? { temperature } : {}), + ...(input.desired.topP !== undefined ? { topP } : {}), + ...(input.desired.maxTokens !== undefined ? { maxTokens } : {}), + ...(input.desired.thinking !== undefined ? { thinking } : {}), + changes, + } } From 613ef8eee8a175ba2a5c57ba8c29929b8d7ba8b7 Mon Sep 17 00:00:00 2001 From: Ravi Tharuma Date: Wed, 25 Mar 2026 15:09:25 +0100 Subject: [PATCH 12/63] fix(model-capabilities): harden runtime capability handling --- src/shared/connected-providers-cache.test.ts | 34 +++++ src/shared/connected-providers-cache.ts | 2 +- src/shared/model-capabilities-cache.test.ts | 31 ++++ src/shared/model-capabilities-cache.ts | 25 ++-- src/shared/model-capabilities.test.ts | 62 ++++++++ src/shared/model-capabilities.ts | 140 ++++++++++++++++-- src/shared/model-capability-heuristics.ts | 4 +- .../model-settings-compatibility.test.ts | 21 +++ 8 files changed, 296 insertions(+), 23 deletions(-) diff --git a/src/shared/connected-providers-cache.test.ts b/src/shared/connected-providers-cache.test.ts index cd2573b93..73c905d25 100644 --- a/src/shared/connected-providers-cache.test.ts +++ b/src/shared/connected-providers-cache.test.ts @@ -229,4 +229,38 @@ describe("updateConnectedProvidersCache", () => { limit: { output: 128000 }, }) }) + + test("keeps normalized fallback ids when raw metadata id is not a string", async () => { + const mockClient = { + provider: { + list: async () => ({ + data: { + connected: ["openai"], + all: [ + { + id: "openai", + models: { + "o3-mini": { + id: 123, + name: "o3-mini", + }, + }, + }, + ], + }, + }), + }, + } + + await testCacheStore.updateConnectedProvidersCache(mockClient) + const cache = testCacheStore.readProviderModelsCache() + + expect(cache?.models.openai).toEqual([ + { id: "o3-mini", name: "o3-mini" }, + ]) + expect(findProviderModelMetadata("openai", "o3-mini", cache)).toEqual({ + id: "o3-mini", + name: "o3-mini", + }) + }) }) diff --git a/src/shared/connected-providers-cache.ts b/src/shared/connected-providers-cache.ts index 61003cd38..cf17852cd 100644 --- a/src/shared/connected-providers-cache.ts +++ b/src/shared/connected-providers-cache.ts @@ -198,8 +198,8 @@ export function createConnectedProvidersCacheStore( : modelID return { - id: normalizedID, ...rawMetadata, + id: normalizedID, } satisfies ModelMetadata }) if (modelMetadata.length > 0) { diff --git a/src/shared/model-capabilities-cache.test.ts b/src/shared/model-capabilities-cache.test.ts index 2575577c3..0773a5fe0 100644 --- a/src/shared/model-capabilities-cache.test.ts +++ b/src/shared/model-capabilities-cache.test.ts @@ -97,6 +97,37 @@ describe("model-capabilities-cache", () => { }) }) + test("merges repeated snapshot entries without materializing empty optional objects", () => { + const raw = { + openai: { + models: { + "gpt-5.4": { + id: "gpt-5.4", + family: "gpt", + }, + }, + }, + alias: { + models: { + "gpt-5.4-preview": { + id: "gpt-5.4", + reasoning: true, + }, + }, + }, + } + + const snapshot = buildModelCapabilitiesSnapshotFromModelsDev(raw) + + expect(snapshot.models["gpt-5.4"]).toEqual({ + id: "gpt-5.4", + family: "gpt", + reasoning: true, + }) + expect(snapshot.models["gpt-5.4"]).not.toHaveProperty("modalities") + expect(snapshot.models["gpt-5.4"]).not.toHaveProperty("limit") + }) + test("refresh writes cache and preserves unrelated files in the cache directory", async () => { //#given const sentinelPath = join(testCacheDir, "keep-me.json") diff --git a/src/shared/model-capabilities-cache.ts b/src/shared/model-capabilities-cache.ts index c3339cd8d..bff841c68 100644 --- a/src/shared/model-capabilities-cache.ts +++ b/src/shared/model-capabilities-cache.ts @@ -8,7 +8,7 @@ export const MODELS_DEV_SOURCE_URL = "https://models.dev/api.json" const MODEL_CAPABILITIES_CACHE_FILE = "model-capabilities.json" function isRecord(value: unknown): value is Record { - return typeof value === "object" && value !== null + return typeof value === "object" && value !== null && !Array.isArray(value) } function readBoolean(value: unknown): boolean | undefined { @@ -84,17 +84,24 @@ function mergeSnapshotEntries( return incoming } + const mergedModalities = existing.modalities || incoming.modalities + ? { + ...existing.modalities, + ...incoming.modalities, + } + : undefined + const mergedLimit = existing.limit || incoming.limit + ? { + ...existing.limit, + ...incoming.limit, + } + : undefined + return { ...existing, ...incoming, - modalities: { - ...existing.modalities, - ...incoming.modalities, - }, - limit: { - ...existing.limit, - ...incoming.limit, - }, + ...(mergedModalities ? { modalities: mergedModalities } : {}), + ...(mergedLimit ? { limit: mergedLimit } : {}), } } diff --git a/src/shared/model-capabilities.test.ts b/src/shared/model-capabilities.test.ts index 172e7a523..82b5ea649 100644 --- a/src/shared/model-capabilities.test.ts +++ b/src/shared/model-capabilities.test.ts @@ -81,6 +81,53 @@ describe("getModelCapabilities", () => { }) }) + test("reads structured runtime capabilities from the SDK v2 shape", () => { + const result = getModelCapabilities({ + providerID: "openai", + modelID: "gpt-5.4", + runtimeModel: { + capabilities: { + reasoning: true, + temperature: false, + toolcall: true, + input: { + text: true, + image: true, + }, + output: { + text: true, + }, + }, + }, + bundledSnapshot, + }) + + expect(result).toMatchObject({ + canonicalModelID: "gpt-5.4", + reasoning: true, + supportsThinking: true, + supportsTemperature: false, + toolCall: true, + modalities: { + input: ["text", "image"], + output: ["text"], + }, + }) + }) + + test("accepts runtime variant arrays without corrupting them into numeric keys", () => { + const result = getModelCapabilities({ + providerID: "openai", + modelID: "gpt-5.4", + runtimeModel: { + variants: ["low", "medium", "high", "xhigh"], + }, + bundledSnapshot, + }) + + expect(result.variants).toEqual(["low", "medium", "high", "xhigh"]) + }) + test("normalizes thinking suffix aliases before snapshot lookup", () => { const result = getModelCapabilities({ providerID: "anthropic", @@ -156,4 +203,19 @@ describe("getModelCapabilities", () => { reasoningEfforts: ["none", "minimal", "low", "medium", "high"], }) }) + + test("detects prefixed o-series model IDs through the heuristic fallback", () => { + const result = getModelCapabilities({ + providerID: "azure-openai", + modelID: "openai/o3-mini", + bundledSnapshot, + }) + + expect(result).toMatchObject({ + canonicalModelID: "openai/o3-mini", + family: "openai-reasoning", + variants: ["low", "medium", "high"], + reasoningEfforts: ["none", "minimal", "low", "medium", "high"], + }) + }) }) diff --git a/src/shared/model-capabilities.ts b/src/shared/model-capabilities.ts index 887d15286..cead7f00e 100644 --- a/src/shared/model-capabilities.ts +++ b/src/shared/model-capabilities.ts @@ -72,7 +72,7 @@ const MODEL_ID_OVERRIDES: Record = { } function isRecord(value: unknown): value is Record { - return typeof value === "object" && value !== null + return typeof value === "object" && value !== null && !Array.isArray(value) } function normalizeLookupModelID(modelID: string): string { @@ -97,6 +97,11 @@ function readStringArray(value: unknown): string[] | undefined { } function normalizeVariantKeys(value: unknown): string[] | undefined { + const arrayVariants = readStringArray(value) + if (arrayVariants) { + return arrayVariants.map((variant) => variant.toLowerCase()) + } + if (!isRecord(value)) { return undefined } @@ -105,13 +110,30 @@ function normalizeVariantKeys(value: unknown): string[] | undefined { return variants.length > 0 ? variants : undefined } +function readModalityKeys(value: unknown): string[] | undefined { + const stringArray = readStringArray(value) + if (stringArray) { + return stringArray.map((entry) => entry.toLowerCase()) + } + + if (!isRecord(value)) { + return undefined + } + + const enabled = Object.entries(value) + .filter(([, supported]) => supported === true) + .map(([modality]) => modality.toLowerCase()) + + return enabled.length > 0 ? enabled : undefined +} + function normalizeModalities(value: unknown): ModelCapabilities["modalities"] | undefined { if (!isRecord(value)) { return undefined } - const input = readStringArray(value.input) - const output = readStringArray(value.output) + const input = readModalityKeys(value.input) + const output = readModalityKeys(value.output) if (!input && !output) { return undefined @@ -145,12 +167,18 @@ function getOverride(modelID: string): ModelCapabilityOverride | undefined { return MODEL_ID_OVERRIDES[normalizeLookupModelID(modelID)] } +function readRuntimeModelCapabilities(runtimeModel: Record | undefined): Record | undefined { + return isRecord(runtimeModel?.capabilities) ? runtimeModel.capabilities : undefined +} + function readRuntimeModelLimitOutput(runtimeModel: Record | undefined): number | undefined { if (!runtimeModel) { return undefined } - const limit = runtimeModel.limit + const limit = isRecord(runtimeModel.limit) + ? runtimeModel.limit + : readRuntimeModelCapabilities(runtimeModel)?.limit if (!isRecord(limit)) { return undefined } @@ -163,11 +191,101 @@ function readRuntimeModelBoolean(runtimeModel: Record | undefin return undefined } + const runtimeCapabilities = readRuntimeModelCapabilities(runtimeModel) + for (const key of keys) { const value = runtimeModel[key] if (typeof value === "boolean") { return value } + + const capabilityValue = runtimeCapabilities?.[key] + if (typeof capabilityValue === "boolean") { + return capabilityValue + } + } + + return undefined +} + +function readRuntimeModelModalities(runtimeModel: Record | undefined): ModelCapabilities["modalities"] | undefined { + if (!runtimeModel) { + return undefined + } + + const rootModalities = normalizeModalities(runtimeModel.modalities) + if (rootModalities) { + return rootModalities + } + + const runtimeCapabilities = readRuntimeModelCapabilities(runtimeModel) + if (!runtimeCapabilities) { + return undefined + } + + const nestedModalities = normalizeModalities(runtimeCapabilities.modalities) + if (nestedModalities) { + return nestedModalities + } + + const capabilityModalities = normalizeModalities(runtimeCapabilities) + if (capabilityModalities) { + return capabilityModalities + } + + return undefined +} + +function readRuntimeModelVariants(runtimeModel: Record | undefined): string[] | undefined { + if (!runtimeModel) { + return undefined + } + + const rootVariants = normalizeVariantKeys(runtimeModel.variants) + if (rootVariants) { + return rootVariants + } + + const runtimeCapabilities = readRuntimeModelCapabilities(runtimeModel) + if (!runtimeCapabilities) { + return undefined + } + + return normalizeVariantKeys(runtimeCapabilities.variants) +} + +function readRuntimeModelTopPSupport(runtimeModel: Record | undefined): boolean | undefined { + return readRuntimeModelBoolean(runtimeModel, ["topP", "top_p"]) +} + +function readRuntimeModelToolCallSupport(runtimeModel: Record | undefined): boolean | undefined { + return readRuntimeModelBoolean(runtimeModel, ["toolCall", "tool_call", "toolcall"]) +} + +function readRuntimeModelReasoningSupport(runtimeModel: Record | undefined): boolean | undefined { + return readRuntimeModelBoolean(runtimeModel, ["reasoning"]) +} + +function readRuntimeModelTemperatureSupport(runtimeModel: Record | undefined): boolean | undefined { + return readRuntimeModelBoolean(runtimeModel, ["temperature"]) +} + +function readRuntimeModelThinkingSupport(runtimeModel: Record | undefined): boolean | undefined { + const capabilityValue = readRuntimeModelReasoningSupport(runtimeModel) + if (capabilityValue !== undefined) { + return capabilityValue + } + + const runtimeCapabilities = readRuntimeModelCapabilities(runtimeModel) + if (!runtimeCapabilities) { + return undefined + } + + for (const key of ["thinking", "supportsThinking"] as const) { + const value = runtimeCapabilities[key] + if (typeof value === "boolean") { + return value + } } return undefined @@ -194,7 +312,7 @@ export function getModelCapabilities(input: GetModelCapabilitiesInput): ModelCap const bundledSnapshot = input.bundledSnapshot ?? bundledModelCapabilitiesSnapshot const snapshotEntry = runtimeSnapshot?.models?.[canonicalModelID] ?? bundledSnapshot.models[canonicalModelID] const heuristicFamily = detectHeuristicModelFamily(canonicalModelID) - const runtimeVariants = normalizeVariantKeys(runtimeModel?.variants) + const runtimeVariants = readRuntimeModelVariants(runtimeModel) return { requestedModelID, @@ -202,27 +320,27 @@ export function getModelCapabilities(input: GetModelCapabilitiesInput): ModelCap family: snapshotEntry?.family ?? heuristicFamily?.family, variants: runtimeVariants ?? override?.variants ?? heuristicFamily?.variants, reasoningEfforts: override?.reasoningEfforts ?? heuristicFamily?.reasoningEfforts, - reasoning: readRuntimeModelBoolean(runtimeModel, ["reasoning"]) ?? snapshotEntry?.reasoning, + reasoning: readRuntimeModelReasoningSupport(runtimeModel) ?? snapshotEntry?.reasoning, supportsThinking: override?.supportsThinking ?? heuristicFamily?.supportsThinking - ?? readRuntimeModelBoolean(runtimeModel, ["reasoning"]) + ?? readRuntimeModelThinkingSupport(runtimeModel) ?? snapshotEntry?.reasoning, supportsTemperature: - readRuntimeModelBoolean(runtimeModel, ["temperature"]) + readRuntimeModelTemperatureSupport(runtimeModel) ?? override?.supportsTemperature ?? snapshotEntry?.temperature, supportsTopP: - readRuntimeModelBoolean(runtimeModel, ["topP", "top_p"]) + readRuntimeModelTopPSupport(runtimeModel) ?? override?.supportsTopP, maxOutputTokens: readRuntimeModelLimitOutput(runtimeModel) ?? snapshotEntry?.limit?.output, toolCall: - readRuntimeModelBoolean(runtimeModel, ["toolCall", "tool_call"]) + readRuntimeModelToolCallSupport(runtimeModel) ?? snapshotEntry?.toolCall, modalities: - normalizeModalities(runtimeModel?.modalities) + readRuntimeModelModalities(runtimeModel) ?? snapshotEntry?.modalities, } } diff --git a/src/shared/model-capability-heuristics.ts b/src/shared/model-capability-heuristics.ts index 73286badc..374c185ea 100644 --- a/src/shared/model-capability-heuristics.ts +++ b/src/shared/model-capability-heuristics.ts @@ -24,14 +24,14 @@ export const HEURISTIC_MODEL_FAMILY_REGISTRY: ReadonlyArray { }) }) + test("GPT-5 downgrades unsupported max variant to xhigh", () => { + const result = resolveCompatibleModelSettings({ + providerID: "openai", + modelID: "gpt-5.4", + desired: { variant: "max" }, + }) + + expect(result).toEqual({ + variant: "xhigh", + reasoningEffort: undefined, + changes: [ + { + field: "variant", + from: "max", + to: "xhigh", + reason: "unsupported-by-model-family", + }, + ], + }) + }) + // Reasoning effort: "none" and "minimal" are valid per Vercel AI SDK test("GPT-5 keeps none reasoningEffort", () => { const result = resolveCompatibleModelSettings({ From f04cc0fa9cb6b587711159aed1c2dfca14831775 Mon Sep 17 00:00:00 2001 From: YeonGyu-Kim Date: Wed, 25 Mar 2026 23:23:46 +0900 Subject: [PATCH 13/63] fix(thinking-block-validator): replace model-name gating with content-based history detection MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace isExtendedThinkingModel() model-name check with hasSignedThinkingBlocksInHistory() which scans message history for real Anthropic-signed thinking blocks. Content-based gating is more robust than model-name checks — works correctly with custom model IDs, proxied models, and new model releases without code changes. - Add isSignedThinkingPart() that matches type thinking/redacted_thinking with valid signature - Skip synthetic parts (injected by previous hook runs) - GPT reasoning blocks (type=reasoning, no signature) correctly excluded - Add comprehensive tests: signed injection, redacted_thinking, reasoning negative case, synthetic skip Inspired by PR #2653 content-based approach, combined with redacted_thinking support from 0732cb85. Ultraworked with Sisyphus Co-authored-by: Sisyphus --- .../thinking-block-validator/hook.test.ts | 222 ++++++++++++------ src/hooks/thinking-block-validator/hook.ts | 120 ++++++---- 2 files changed, 219 insertions(+), 123 deletions(-) diff --git a/src/hooks/thinking-block-validator/hook.test.ts b/src/hooks/thinking-block-validator/hook.test.ts index 9eab6dff6..0601cbcfc 100644 --- a/src/hooks/thinking-block-validator/hook.test.ts +++ b/src/hooks/thinking-block-validator/hook.test.ts @@ -1,108 +1,184 @@ -const { describe, expect, test } = require("bun:test") +declare const describe: (name: string, fn: () => void) => void +declare const it: (name: string, fn: () => void | Promise) => void +declare const expect: (value: T) => { + toBe(expected: T): void + toEqual(expected: unknown): void + toHaveLength(expected: number): void +} -const { createThinkingBlockValidatorHook } = require("./hook") +import { createThinkingBlockValidatorHook } from "./hook" type TestPart = { type: string - id: string text?: string thinking?: string - data?: string signature?: string + synthetic?: boolean } type TestMessage = { - info: { - role: string - id?: string - modelID?: string - } + info: { role: "assistant" | "user" } parts: TestPart[] } -function createMessage(info: TestMessage["info"], parts: TestPart[]): TestMessage { - return { info, parts } -} +async function runTransform(messages: TestMessage[]): Promise { + const hook = createThinkingBlockValidatorHook() + const transform = hook["experimental.chat.messages.transform"] -function createTextPart(id: string, text: string): TestPart { - return { type: "text", id, text } -} + if (!transform) { + throw new Error("missing thinking block validator transform") + } -function createSignedThinkingPart(id: string, thinking: string, signature: string): TestPart { - return { type: "thinking", id, thinking, signature } -} - -function createRedactedThinkingPart(id: string, signature: string): TestPart { - return { type: "redacted_thinking", id, data: "encrypted", signature } + await transform({}, { messages: messages as never }) } describe("createThinkingBlockValidatorHook", () => { - test("reuses the previous signed thinking part verbatim when assistant content lacks a leading thinking block", async () => { - const transform = Reflect.get(createThinkingBlockValidatorHook(), "experimental.chat.messages.transform") - expect(typeof transform).toBe("function") + it("injects signed thinking history verbatim", async () => { + //#given + const signedThinkingPart: TestPart = { + type: "thinking", + thinking: "plan", + signature: "signed-thinking", + } + const messages = [ + { + info: { role: "assistant" }, + parts: [signedThinkingPart], + }, + { + info: { role: "assistant" }, + parts: [{ type: "text", text: "continue" }], + }, + ] satisfies TestMessage[] - const previousThinkingPart = createSignedThinkingPart("prt_prev_signed", "prior reasoning", "sig_prev") - const targetTextPart = createTextPart("prt_target_text", "tool result") - const messages: TestMessage[] = [ - createMessage({ role: "user", modelID: "claude-opus-4-6-thinking" }, [createTextPart("prt_user_text", "continue")]), - createMessage({ role: "assistant", id: "msg_prev" }, [previousThinkingPart, createTextPart("prt_prev_text", "done")]), - createMessage({ role: "assistant", id: "msg_target" }, [targetTextPart]), - ] + //#when + await runTransform(messages) - await Reflect.apply(transform, undefined, [{}, { messages }]) - - expect(messages[2]?.parts[0]).toBe(previousThinkingPart) - expect(messages[2]?.parts).toEqual([previousThinkingPart, targetTextPart]) + //#then + expect(messages[1]?.parts[0]).toBe(signedThinkingPart) }) - test("skips injection when no signed Anthropic thinking part exists in history", async () => { - const transform = Reflect.get(createThinkingBlockValidatorHook(), "experimental.chat.messages.transform") - expect(typeof transform).toBe("function") + it("injects signed redacted_thinking history verbatim", async () => { + //#given + const signedRedactedThinkingPart: TestPart = { + type: "redacted_thinking", + signature: "signed-redacted-thinking", + } + const messages = [ + { + info: { role: "assistant" }, + parts: [signedRedactedThinkingPart], + }, + { + info: { role: "assistant" }, + parts: [{ type: "tool_use" }], + }, + ] satisfies TestMessage[] - const targetTextPart = createTextPart("prt_target_text", "tool result") - const messages: TestMessage[] = [ - createMessage({ role: "user", modelID: "claude-opus-4-6-thinking" }, [createTextPart("prt_user_text", "continue")]), - createMessage({ role: "assistant", id: "msg_prev" }, [{ type: "reasoning", id: "prt_reason", text: "gpt reasoning" }]), - createMessage({ role: "assistant", id: "msg_target" }, [targetTextPart]), - ] + //#when + await runTransform(messages) - await Reflect.apply(transform, undefined, [{}, { messages }]) - - expect(messages[2]?.parts).toEqual([targetTextPart]) + //#then + expect(messages[1]?.parts[0]).toBe(signedRedactedThinkingPart) }) - test("does not inject when the assistant message already starts with redacted thinking", async () => { - const transform = Reflect.get(createThinkingBlockValidatorHook(), "experimental.chat.messages.transform") - expect(typeof transform).toBe("function") + it("skips hook when history contains reasoning only", async () => { + //#given + const reasoningPart: TestPart = { + type: "reasoning", + text: "internal reasoning", + } + const messages = [ + { + info: { role: "assistant" }, + parts: [reasoningPart], + }, + { + info: { role: "assistant" }, + parts: [{ type: "text", text: "continue" }], + }, + ] satisfies TestMessage[] - const existingThinkingPart = createRedactedThinkingPart("prt_redacted", "sig_redacted") - const targetTextPart = createTextPart("prt_target_text", "tool result") - const messages: TestMessage[] = [ - createMessage({ role: "user", modelID: "claude-opus-4-6-thinking" }, [createTextPart("prt_user_text", "continue")]), - createMessage({ role: "assistant", id: "msg_target" }, [existingThinkingPart, targetTextPart]), - ] + //#when + await runTransform(messages) - await Reflect.apply(transform, undefined, [{}, { messages }]) - - expect(messages[1]?.parts).toEqual([existingThinkingPart, targetTextPart]) + //#then + expect(messages[1]?.parts).toEqual([{ type: "text", text: "continue" }]) }) - test("skips processing for models without extended thinking", async () => { - const transform = Reflect.get(createThinkingBlockValidatorHook(), "experimental.chat.messages.transform") - expect(typeof transform).toBe("function") + it("skips hook when no signed history exists", async () => { + //#given + const messages = [ + { + info: { role: "assistant" }, + parts: [{ type: "thinking", thinking: "draft" }], + }, + { + info: { role: "assistant" }, + parts: [{ type: "text", text: "continue" }], + }, + ] satisfies TestMessage[] - const previousThinkingPart = createSignedThinkingPart("prt_prev_signed", "prior reasoning", "sig_prev") - const targetTextPart = createTextPart("prt_target_text", "tool result") - const messages: TestMessage[] = [ - createMessage({ role: "user", modelID: "gpt-5.4" }, [createTextPart("prt_user_text", "continue")]), - createMessage({ role: "assistant", id: "msg_prev" }, [previousThinkingPart]), - createMessage({ role: "assistant", id: "msg_target" }, [targetTextPart]), - ] + //#when + await runTransform(messages) - await Reflect.apply(transform, undefined, [{}, { messages }]) + //#then + expect(messages[1]?.parts).toEqual([{ type: "text", text: "continue" }]) + }) - expect(messages[2]?.parts).toEqual([targetTextPart]) + it("skips hook when history contains synthetic signed blocks only", async () => { + //#given + const syntheticSignedPart: TestPart = { + type: "thinking", + thinking: "synthetic", + signature: "synthetic-signature", + synthetic: true, + } + const messages = [ + { + info: { role: "assistant" }, + parts: [syntheticSignedPart], + }, + { + info: { role: "assistant" }, + parts: [{ type: "text", text: "continue" }], + }, + ] satisfies TestMessage[] + + //#when + await runTransform(messages) + + //#then + expect(messages[1]?.parts).toEqual([{ type: "text", text: "continue" }]) + }) + + it("does not reinject when the message already starts with redacted_thinking", async () => { + //#given + const signedThinkingPart: TestPart = { + type: "thinking", + thinking: "plan", + signature: "signed-thinking", + } + const leadingRedactedThinkingPart: TestPart = { + type: "redacted_thinking", + signature: "existing-redacted-thinking", + } + const messages = [ + { + info: { role: "assistant" }, + parts: [signedThinkingPart], + }, + { + info: { role: "assistant" }, + parts: [leadingRedactedThinkingPart, { type: "text", text: "continue" }], + }, + ] satisfies TestMessage[] + + //#when + await runTransform(messages) + + //#then + expect(messages[1]?.parts[0]).toBe(leadingRedactedThinkingPart) + expect(messages[1]?.parts).toHaveLength(2) }) }) - -export {} diff --git a/src/hooks/thinking-block-validator/hook.ts b/src/hooks/thinking-block-validator/hook.ts index 67b6c12c0..544d8e672 100644 --- a/src/hooks/thinking-block-validator/hook.ts +++ b/src/hooks/thinking-block-validator/hook.ts @@ -21,11 +21,6 @@ interface MessageWithParts { parts: Part[] } -type SignedThinkingPart = Part & { - type: "thinking" | "redacted_thinking" - signature: string -} - type MessagesTransformHook = { "experimental.chat.messages.transform"?: ( input: Record, @@ -33,25 +28,39 @@ type MessagesTransformHook = { ) => Promise } -/** - * Check if a model has extended thinking enabled - * Uses patterns from think-mode/switcher.ts for consistency - */ -function isExtendedThinkingModel(modelID: string): boolean { - if (!modelID) return false - const lower = modelID.toLowerCase() +type SignedThinkingPart = Part & { + type: "thinking" | "redacted_thinking" + thinking?: string + signature: string + synthetic?: boolean +} - // Check for explicit thinking/high variants (always enabled) - if (lower.includes("thinking") || lower.endsWith("-high")) { - return true +function isSignedThinkingPart(part: Part): part is SignedThinkingPart { + const type = part.type as string + if (type !== "thinking" && type !== "redacted_thinking") { + return false } - // Check for thinking-capable models (claude-4 family, claude-3) - // Aligns with THINKING_CAPABLE_MODELS in think-mode/switcher.ts - return ( - lower.includes("claude-sonnet-4") || - lower.includes("claude-opus-4") || - lower.includes("claude-3") + const signature = (part as { signature?: unknown }).signature + const synthetic = (part as { synthetic?: unknown }).synthetic + return typeof signature === "string" && signature.length > 0 && synthetic !== true +} + +/** + * Check if there are any Anthropic-signed thinking blocks in the message history. + * + * Only returns true for real `type: "thinking"` blocks with a valid `signature`. + * GPT reasoning blocks (`type: "reasoning"`) are intentionally excluded — they + * have no Anthropic signature and must never be forwarded to the Anthropic API. + * + * Model-name checks are unreliable (miss GPT+thinking, custom model IDs, etc.) + * so we inspect the messages themselves. + */ +function hasSignedThinkingBlocksInHistory(messages: MessageWithParts[]): boolean { + return messages.some( + m => + m.info.role === "assistant" && + m.parts?.some((p: Part) => isSignedThinkingPart(p)), ) } @@ -79,36 +88,42 @@ function startsWithThinkingBlock(parts: Part[]): boolean { return type === "thinking" || type === "redacted_thinking" || type === "reasoning" } -function isSignedThinkingPart(part: Part): part is SignedThinkingPart { - const type = part.type as string - if (type !== "thinking" && type !== "redacted_thinking") { - return false - } - - const signature = (part as { signature?: unknown }).signature - return typeof signature === "string" && signature.length > 0 -} - -function findPreviousThinkingPart( - messages: MessageWithParts[], - currentIndex: number -): SignedThinkingPart | null { +/** + * Find the most recent Anthropic-signed thinking part from previous assistant messages. + * + * Returns the original Part object (including its `signature` field) so it can + * be reused verbatim in another message. Only `type: "thinking"` blocks with + * both a `signature` and `thinking` field are returned — GPT `type: "reasoning"` + * blocks are excluded because they lack an Anthropic signature and would be + * rejected by the API with "Invalid `signature` in `thinking` block". + * Synthetic parts injected by a previous run of this hook are also skipped. + */ +function findPreviousThinkingPart(messages: MessageWithParts[], currentIndex: number): SignedThinkingPart | null { // Search backwards from current message for (let i = currentIndex - 1; i >= 0; i--) { const msg = messages[i] if (msg.info.role !== "assistant") continue - if (!msg.parts) continue + for (const part of msg.parts) { - if (isSignedThinkingPart(part)) { - return part - } + // Only Anthropic thinking blocks — type must be "thinking", not "reasoning" + if (!isSignedThinkingPart(part)) continue + + return part } } return null } +/** + * Prepend an existing thinking block (with its original signature) to a + * message's parts array. + * + * We reuse the original Part verbatim instead of creating a new one, because + * the Anthropic API validates the `signature` field against the thinking + * content. Any synthetic block we create ourselves would fail that check. + */ function prependThinkingBlock(message: MessageWithParts, thinkingPart: SignedThinkingPart): void { if (!message.parts) { message.parts = [] @@ -129,13 +144,12 @@ export function createThinkingBlockValidatorHook(): MessagesTransformHook { return } - // Get the model info from the last user message - const lastUserMessage = messages.findLast(m => m.info.role === "user") - const modelIDValue = (lastUserMessage?.info as { modelID?: unknown } | undefined)?.modelID - const modelID = typeof modelIDValue === "string" ? modelIDValue : "" - - // Only process if extended thinking might be enabled - if (!isExtendedThinkingModel(modelID)) { + // Skip if there are no Anthropic-signed thinking blocks in history. + // This is more reliable than checking model names — works for Claude, + // GPT with thinking variants, or any future model. Crucially, GPT + // reasoning blocks (type="reasoning", no signature) do NOT trigger this + // hook — only real Anthropic thinking blocks do. + if (!hasSignedThinkingBlocksInHistory(messages)) { return } @@ -148,12 +162,18 @@ export function createThinkingBlockValidatorHook(): MessagesTransformHook { // Check if message has content parts but doesn't start with thinking if (hasContentParts(msg.parts) && !startsWithThinkingBlock(msg.parts)) { + // Find the most recent real thinking part (with valid signature) from + // previous turns. If none exists we cannot safely inject a thinking + // block — a synthetic block without a signature would cause the API + // to reject the request with "Invalid `signature` in `thinking` block". const previousThinkingPart = findPreviousThinkingPart(messages, i) - if (!previousThinkingPart) { - continue - } - prependThinkingBlock(msg, previousThinkingPart) + if (previousThinkingPart) { + prependThinkingBlock(msg, previousThinkingPart) + } + // If no real thinking part is available, skip injection entirely. + // The downstream error (if any) is preferable to a guaranteed API + // rejection caused by a signature-less synthetic thinking block. } } }, From 7c0289d7bc00d4c0e0368f153859e22a6374581d Mon Sep 17 00:00:00 2001 From: Ravi Tharuma Date: Wed, 25 Mar 2026 15:41:12 +0100 Subject: [PATCH 14/63] fix(model-capabilities): honor root thinking flags --- src/shared/model-capabilities.test.ts | 16 ++++++++++++++++ src/shared/model-capabilities.ts | 5 +++++ 2 files changed, 21 insertions(+) diff --git a/src/shared/model-capabilities.test.ts b/src/shared/model-capabilities.test.ts index 82b5ea649..a145aab45 100644 --- a/src/shared/model-capabilities.test.ts +++ b/src/shared/model-capabilities.test.ts @@ -115,6 +115,22 @@ describe("getModelCapabilities", () => { }) }) + test("respects root-level thinking flags when providers do not nest them under capabilities", () => { + const result = getModelCapabilities({ + providerID: "custom-proxy", + modelID: "gpt-5.4", + runtimeModel: { + supportsThinking: true, + }, + bundledSnapshot, + }) + + expect(result).toMatchObject({ + canonicalModelID: "gpt-5.4", + supportsThinking: true, + }) + }) + test("accepts runtime variant arrays without corrupting them into numeric keys", () => { const result = getModelCapabilities({ providerID: "openai", diff --git a/src/shared/model-capabilities.ts b/src/shared/model-capabilities.ts index cead7f00e..25835f5d5 100644 --- a/src/shared/model-capabilities.ts +++ b/src/shared/model-capabilities.ts @@ -276,6 +276,11 @@ function readRuntimeModelThinkingSupport(runtimeModel: Record | return capabilityValue } + const rootThinkingSupport = readRuntimeModelBoolean(runtimeModel, ["thinking", "supportsThinking"]) + if (rootThinkingSupport !== undefined) { + return rootThinkingSupport + } + const runtimeCapabilities = readRuntimeModelCapabilities(runtimeModel) if (!runtimeCapabilities) { return undefined From a15f6076bcf403fc8e544d3ce9a131c937e4d595 Mon Sep 17 00:00:00 2001 From: Ravi Tharuma Date: Wed, 25 Mar 2026 16:14:19 +0100 Subject: [PATCH 15/63] feat(model-capabilities): add maintenance guardrails --- .../workflows/refresh-model-capabilities.yml | 43 +++++ .../doctor/checks/model-resolution-details.ts | 8 +- .../doctor/checks/model-resolution-types.ts | 3 + .../doctor/checks/model-resolution.test.ts | 30 ++++ src/cli/doctor/checks/model-resolution.ts | 85 +++++++-- src/shared/model-capabilities.test.ts | 73 ++++++++ src/shared/model-capabilities.ts | 163 +++++++++++++++--- src/shared/model-capability-aliases.test.ts | 37 ++++ src/shared/model-capability-aliases.ts | 84 +++++++++ 9 files changed, 485 insertions(+), 41 deletions(-) create mode 100644 .github/workflows/refresh-model-capabilities.yml create mode 100644 src/shared/model-capability-aliases.test.ts create mode 100644 src/shared/model-capability-aliases.ts diff --git a/.github/workflows/refresh-model-capabilities.yml b/.github/workflows/refresh-model-capabilities.yml new file mode 100644 index 000000000..5d2d053fa --- /dev/null +++ b/.github/workflows/refresh-model-capabilities.yml @@ -0,0 +1,43 @@ +name: Refresh Model Capabilities + +on: + schedule: + - cron: "17 4 * * 1" + workflow_dispatch: + +permissions: + contents: write + pull-requests: write + +jobs: + refresh: + runs-on: ubuntu-latest + if: github.repository == 'code-yeongyu/oh-my-openagent' + steps: + - uses: actions/checkout@v4 + + - uses: oven-sh/setup-bun@v2 + with: + bun-version: latest + + - name: Install dependencies + run: bun install + env: + BUN_INSTALL_ALLOW_SCRIPTS: "@ast-grep/napi" + + - name: Refresh bundled model capabilities snapshot + run: bun run build:model-capabilities + + - name: Create refresh pull request + uses: peter-evans/create-pull-request@v7 + with: + commit-message: "chore: refresh model capabilities snapshot" + title: "chore: refresh model capabilities snapshot" + body: | + Automated refresh of `src/generated/model-capabilities.generated.json` from `https://models.dev/api.json`. + + This keeps the bundled capability snapshot aligned with upstream model metadata without relying on manual refreshes. + branch: automation/refresh-model-capabilities + delete-branch: true + labels: | + maintenance diff --git a/src/cli/doctor/checks/model-resolution-details.ts b/src/cli/doctor/checks/model-resolution-details.ts index e96655476..3443e92b1 100644 --- a/src/cli/doctor/checks/model-resolution-details.ts +++ b/src/cli/doctor/checks/model-resolution-details.ts @@ -4,6 +4,10 @@ import { getOpenCodeCacheDir } from "../../../shared" import type { AvailableModelsInfo, ModelResolutionInfo, OmoConfig } from "./model-resolution-types" import { formatModelWithVariant, getCategoryEffectiveVariant, getEffectiveVariant } from "./model-resolution-variant" +function formatCapabilityResolutionLabel(mode: string | undefined): string { + return mode ?? "unknown" +} + export function buildModelResolutionDetails(options: { info: ModelResolutionInfo available: AvailableModelsInfo @@ -37,7 +41,7 @@ export function buildModelResolutionDetails(options: { agent.effectiveModel, getEffectiveVariant(agent.name, agent.requirement, options.config) ) - details.push(` ${marker} ${agent.name}: ${display}`) + details.push(` ${marker} ${agent.name}: ${display} [capabilities: ${formatCapabilityResolutionLabel(agent.capabilityDiagnostics?.resolutionMode)}]`) } details.push("") details.push("Categories:") @@ -47,7 +51,7 @@ export function buildModelResolutionDetails(options: { category.effectiveModel, getCategoryEffectiveVariant(category.name, category.requirement, options.config) ) - details.push(` ${marker} ${category.name}: ${display}`) + details.push(` ${marker} ${category.name}: ${display} [capabilities: ${formatCapabilityResolutionLabel(category.capabilityDiagnostics?.resolutionMode)}]`) } details.push("") details.push("● = user override, ○ = provider fallback") diff --git a/src/cli/doctor/checks/model-resolution-types.ts b/src/cli/doctor/checks/model-resolution-types.ts index c0396d958..2e77fddd1 100644 --- a/src/cli/doctor/checks/model-resolution-types.ts +++ b/src/cli/doctor/checks/model-resolution-types.ts @@ -1,3 +1,4 @@ +import type { ModelCapabilitiesDiagnostics } from "../../../shared/model-capabilities" import type { ModelRequirement } from "../../../shared/model-requirements" export interface AgentResolutionInfo { @@ -7,6 +8,7 @@ export interface AgentResolutionInfo { userVariant?: string effectiveModel: string effectiveResolution: string + capabilityDiagnostics?: ModelCapabilitiesDiagnostics } export interface CategoryResolutionInfo { @@ -16,6 +18,7 @@ export interface CategoryResolutionInfo { userVariant?: string effectiveModel: string effectiveResolution: string + capabilityDiagnostics?: ModelCapabilitiesDiagnostics } export interface ModelResolutionInfo { diff --git a/src/cli/doctor/checks/model-resolution.test.ts b/src/cli/doctor/checks/model-resolution.test.ts index 902c92cfe..696e8c4d4 100644 --- a/src/cli/doctor/checks/model-resolution.test.ts +++ b/src/cli/doctor/checks/model-resolution.test.ts @@ -129,6 +129,19 @@ describe("model-resolution check", () => { expect(visual!.userOverride).toBe("google/gemini-3-flash-preview") expect(visual!.userVariant).toBe("high") }) + + it("attaches snapshot-backed capability diagnostics for built-in models", async () => { + const { getModelResolutionInfoWithOverrides } = await import("./model-resolution") + + const info = getModelResolutionInfoWithOverrides({}) + const sisyphus = info.agents.find((a) => a.name === "sisyphus") + + expect(sisyphus).toBeDefined() + expect(sisyphus!.capabilityDiagnostics).toMatchObject({ + resolutionMode: "snapshot-backed", + snapshot: { source: "bundled-snapshot" }, + }) + }) }) describe("checkModelResolution", () => { @@ -162,6 +175,23 @@ describe("model-resolution check", () => { expect(result.details!.some((d) => d.includes("Categories:"))).toBe(true) // Should have legend expect(result.details!.some((d) => d.includes("user override"))).toBe(true) + expect(result.details!.some((d) => d.includes("capabilities: snapshot-backed"))).toBe(true) + }) + + it("collects warnings when configured models rely on compatibility fallback", async () => { + const { collectCapabilityResolutionIssues, getModelResolutionInfoWithOverrides } = await import("./model-resolution") + + const info = getModelResolutionInfoWithOverrides({ + agents: { + oracle: { model: "custom/unknown-llm" }, + }, + }) + + const issues = collectCapabilityResolutionIssues(info) + + expect(issues).toHaveLength(1) + expect(issues[0]?.title).toContain("compatibility fallback") + expect(issues[0]?.description).toContain("oracle=custom/unknown-llm") }) }) diff --git a/src/cli/doctor/checks/model-resolution.ts b/src/cli/doctor/checks/model-resolution.ts index c9cc0c0b0..706b18f4b 100644 --- a/src/cli/doctor/checks/model-resolution.ts +++ b/src/cli/doctor/checks/model-resolution.ts @@ -1,4 +1,5 @@ import { AGENT_MODEL_REQUIREMENTS, CATEGORY_MODEL_REQUIREMENTS } from "../../../shared/model-requirements" +import { getModelCapabilities } from "../../../shared/model-capabilities" import { CHECK_IDS, CHECK_NAMES } from "../constants" import type { CheckResult, DoctorIssue } from "../types" import { loadAvailableModelsFromCache } from "./model-resolution-cache" @@ -7,16 +8,36 @@ import { buildModelResolutionDetails } from "./model-resolution-details" import { buildEffectiveResolution, getEffectiveModel } from "./model-resolution-effective-model" import type { AgentResolutionInfo, CategoryResolutionInfo, ModelResolutionInfo, OmoConfig } from "./model-resolution-types" -export function getModelResolutionInfo(): ModelResolutionInfo { - const agents: AgentResolutionInfo[] = Object.entries(AGENT_MODEL_REQUIREMENTS).map(([name, requirement]) => ({ - name, - requirement, - effectiveModel: getEffectiveModel(requirement), - effectiveResolution: buildEffectiveResolution(requirement), - })) +function parseProviderModel(value: string): { providerID: string; modelID: string } | null { + const slashIndex = value.indexOf("/") + if (slashIndex <= 0 || slashIndex === value.length - 1) { + return null + } - const categories: CategoryResolutionInfo[] = Object.entries(CATEGORY_MODEL_REQUIREMENTS).map( - ([name, requirement]) => ({ + return { + providerID: value.slice(0, slashIndex), + modelID: value.slice(slashIndex + 1), + } +} + +function attachCapabilityDiagnostics(entry: T): T { + const parsed = parseProviderModel(entry.effectiveModel) + if (!parsed) { + return entry + } + + return { + ...entry, + capabilityDiagnostics: getModelCapabilities({ + providerID: parsed.providerID, + modelID: parsed.modelID, + }).diagnostics, + } +} + +export function getModelResolutionInfo(): ModelResolutionInfo { + const agents: AgentResolutionInfo[] = Object.entries(AGENT_MODEL_REQUIREMENTS).map(([name, requirement]) => + attachCapabilityDiagnostics({ name, requirement, effectiveModel: getEffectiveModel(requirement), @@ -24,6 +45,16 @@ export function getModelResolutionInfo(): ModelResolutionInfo { }) ) + const categories: CategoryResolutionInfo[] = Object.entries(CATEGORY_MODEL_REQUIREMENTS).map( + ([name, requirement]) => + attachCapabilityDiagnostics({ + name, + requirement, + effectiveModel: getEffectiveModel(requirement), + effectiveResolution: buildEffectiveResolution(requirement), + }) + ) + return { agents, categories } } @@ -31,34 +62,60 @@ export function getModelResolutionInfoWithOverrides(config: OmoConfig): ModelRes const agents: AgentResolutionInfo[] = Object.entries(AGENT_MODEL_REQUIREMENTS).map(([name, requirement]) => { const userOverride = config.agents?.[name]?.model const userVariant = config.agents?.[name]?.variant - return { + return attachCapabilityDiagnostics({ name, requirement, userOverride, userVariant, effectiveModel: getEffectiveModel(requirement, userOverride), effectiveResolution: buildEffectiveResolution(requirement, userOverride), - } + }) }) const categories: CategoryResolutionInfo[] = Object.entries(CATEGORY_MODEL_REQUIREMENTS).map( ([name, requirement]) => { const userOverride = config.categories?.[name]?.model const userVariant = config.categories?.[name]?.variant - return { + return attachCapabilityDiagnostics({ name, requirement, userOverride, userVariant, effectiveModel: getEffectiveModel(requirement, userOverride), effectiveResolution: buildEffectiveResolution(requirement, userOverride), - } + }) } ) return { agents, categories } } +export function collectCapabilityResolutionIssues(info: ModelResolutionInfo): DoctorIssue[] { + const issues: DoctorIssue[] = [] + const allEntries = [...info.agents, ...info.categories] + const fallbackEntries = allEntries.filter((entry) => { + const mode = entry.capabilityDiagnostics?.resolutionMode + return mode === "alias-backed" || mode === "heuristic-backed" || mode === "unknown" + }) + + if (fallbackEntries.length === 0) { + return issues + } + + const summary = fallbackEntries + .map((entry) => `${entry.name}=${entry.effectiveModel} (${entry.capabilityDiagnostics?.resolutionMode ?? "unknown"})`) + .join(", ") + + issues.push({ + title: "Configured models rely on compatibility fallback", + description: summary, + severity: "warning", + affects: fallbackEntries.map((entry) => entry.name), + }) + + return issues +} + export async function checkModels(): Promise { const config = loadOmoConfig() ?? {} const info = getModelResolutionInfoWithOverrides(config) @@ -75,6 +132,8 @@ export async function checkModels(): Promise { }) } + issues.push(...collectCapabilityResolutionIssues(info)) + const overrideCount = info.agents.filter((agent) => Boolean(agent.userOverride)).length + info.categories.filter((category) => Boolean(category.userOverride)).length diff --git a/src/shared/model-capabilities.test.ts b/src/shared/model-capabilities.test.ts index a145aab45..afad4ba0a 100644 --- a/src/shared/model-capabilities.test.ts +++ b/src/shared/model-capabilities.test.ts @@ -2,8 +2,10 @@ import { describe, expect, test } from "bun:test" import { getModelCapabilities, + getBundledModelCapabilitiesSnapshot, type ModelCapabilitiesSnapshot, } from "./model-capabilities" +import { AGENT_MODEL_REQUIREMENTS, CATEGORY_MODEL_REQUIREMENTS } from "./model-requirements" describe("getModelCapabilities", () => { const bundledSnapshot: ModelCapabilitiesSnapshot = { @@ -79,6 +81,12 @@ describe("getModelCapabilities", () => { maxOutputTokens: 128_000, toolCall: true, }) + expect(result.diagnostics).toMatchObject({ + resolutionMode: "snapshot-backed", + canonicalization: { source: "canonical" }, + snapshot: { source: "bundled-snapshot" }, + variants: { source: "runtime" }, + }) }) test("reads structured runtime capabilities from the SDK v2 shape", () => { @@ -113,6 +121,12 @@ describe("getModelCapabilities", () => { output: ["text"], }, }) + expect(result.diagnostics).toMatchObject({ + resolutionMode: "snapshot-backed", + reasoning: { source: "runtime" }, + supportsThinking: { source: "runtime" }, + toolCall: { source: "runtime" }, + }) }) test("respects root-level thinking flags when providers do not nest them under capabilities", () => { @@ -129,6 +143,9 @@ describe("getModelCapabilities", () => { canonicalModelID: "gpt-5.4", supportsThinking: true, }) + expect(result.diagnostics).toMatchObject({ + supportsThinking: { source: "runtime" }, + }) }) test("accepts runtime variant arrays without corrupting them into numeric keys", () => { @@ -158,6 +175,14 @@ describe("getModelCapabilities", () => { supportsTemperature: true, maxOutputTokens: 128_000, }) + expect(result.diagnostics).toMatchObject({ + resolutionMode: "alias-backed", + canonicalization: { + source: "pattern-alias", + ruleID: "anthropic-thinking-suffix", + }, + snapshot: { source: "bundled-snapshot" }, + }) }) test("maps local gemini aliases to canonical models.dev entries", () => { @@ -174,6 +199,14 @@ describe("getModelCapabilities", () => { supportsTemperature: true, maxOutputTokens: 65_000, }) + expect(result.diagnostics).toMatchObject({ + resolutionMode: "alias-backed", + canonicalization: { + source: "exact-alias", + ruleID: "gemini-3.1-pro-tier-alias", + }, + snapshot: { source: "bundled-snapshot" }, + }) }) test("prefers runtime models.dev cache over bundled snapshot", () => { @@ -203,6 +236,11 @@ describe("getModelCapabilities", () => { maxOutputTokens: 64_000, supportsTemperature: false, }) + expect(result.diagnostics).toMatchObject({ + snapshot: { source: "runtime-snapshot" }, + maxOutputTokens: { source: "runtime-snapshot" }, + supportsTemperature: { source: "runtime-snapshot" }, + }) }) test("falls back to heuristic family rules when no snapshot entry exists", () => { @@ -218,6 +256,12 @@ describe("getModelCapabilities", () => { variants: ["low", "medium", "high"], reasoningEfforts: ["none", "minimal", "low", "medium", "high"], }) + expect(result.diagnostics).toMatchObject({ + resolutionMode: "heuristic-backed", + snapshot: { source: "none" }, + family: { source: "heuristic" }, + reasoningEfforts: { source: "heuristic" }, + }) }) test("detects prefixed o-series model IDs through the heuristic fallback", () => { @@ -233,5 +277,34 @@ describe("getModelCapabilities", () => { variants: ["low", "medium", "high"], reasoningEfforts: ["none", "minimal", "low", "medium", "high"], }) + expect(result.diagnostics).toMatchObject({ + resolutionMode: "heuristic-backed", + snapshot: { source: "none" }, + family: { source: "heuristic" }, + }) + }) + + test("keeps every built-in OmO requirement model snapshot-backed", () => { + const bundledSnapshot = getBundledModelCapabilitiesSnapshot() + const requirementModels = new Set() + + for (const requirement of Object.values(AGENT_MODEL_REQUIREMENTS)) { + for (const entry of requirement.fallbackChain) requirementModels.add(entry.model) + } + + for (const requirement of Object.values(CATEGORY_MODEL_REQUIREMENTS)) { + for (const entry of requirement.fallbackChain) requirementModels.add(entry.model) + } + + for (const modelID of requirementModels) { + const result = getModelCapabilities({ + providerID: "test-provider", + modelID, + bundledSnapshot, + }) + + expect(result.diagnostics.resolutionMode).toBe("snapshot-backed") + expect(result.diagnostics.snapshot.source).toBe("bundled-snapshot") + } }) }) diff --git a/src/shared/model-capabilities.ts b/src/shared/model-capabilities.ts index 25835f5d5..0a9749243 100644 --- a/src/shared/model-capabilities.ts +++ b/src/shared/model-capabilities.ts @@ -1,5 +1,6 @@ import bundledModelCapabilitiesSnapshotJson from "../generated/model-capabilities.generated.json" import { findProviderModelMetadata, type ModelMetadata } from "./connected-providers-cache" +import { resolveModelIDAlias } from "./model-capability-aliases" import { detectHeuristicModelFamily } from "./model-capability-heuristics" export type ModelCapabilitiesSnapshotEntry = { @@ -41,6 +42,7 @@ export type ModelCapabilities = { input?: string[] output?: string[] } + diagnostics: ModelCapabilitiesDiagnostics } type GetModelCapabilitiesInput = { @@ -52,7 +54,6 @@ type GetModelCapabilitiesInput = { } type ModelCapabilityOverride = { - canonicalModelID?: string variants?: string[] reasoningEfforts?: string[] supportsThinking?: boolean @@ -60,17 +61,40 @@ type ModelCapabilityOverride = { supportsTopP?: boolean } -const MODEL_ID_OVERRIDES: Record = { - "claude-opus-4-6-thinking": { canonicalModelID: "claude-opus-4-6" }, - "claude-sonnet-4-6-thinking": { canonicalModelID: "claude-sonnet-4-6" }, - "claude-opus-4-5-thinking": { canonicalModelID: "claude-opus-4-5-20251101" }, - "gpt-5.3-codex-spark": { canonicalModelID: "gpt-5.3-codex" }, - "gemini-3.1-pro-high": { canonicalModelID: "gemini-3.1-pro-preview" }, - "gemini-3.1-pro-low": { canonicalModelID: "gemini-3.1-pro-preview" }, - "gemini-3-pro-high": { canonicalModelID: "gemini-3-pro-preview" }, - "gemini-3-pro-low": { canonicalModelID: "gemini-3-pro-preview" }, +type DiagnosticSource = + | "none" + | "runtime" + | "runtime-snapshot" + | "bundled-snapshot" + | "override" + | "heuristic" + | "canonical" + | "exact-alias" + | "pattern-alias" + +export type ModelCapabilitiesDiagnostics = { + resolutionMode: "snapshot-backed" | "alias-backed" | "heuristic-backed" | "unknown" + canonicalization: { + source: "canonical" | "exact-alias" | "pattern-alias" + ruleID?: string + } + snapshot: { + source: "runtime-snapshot" | "bundled-snapshot" | "none" + } + family: { source: "snapshot" | "heuristic" | "none" } + variants: { source: Exclude } + reasoningEfforts: { source: Exclude } + reasoning: { source: "runtime" | "runtime-snapshot" | "bundled-snapshot" | "none" } + supportsThinking: { source: "runtime" | "override" | "heuristic" | "runtime-snapshot" | "bundled-snapshot" | "none" } + supportsTemperature: { source: "runtime" | "override" | "runtime-snapshot" | "bundled-snapshot" | "none" } + supportsTopP: { source: "runtime" | "override" | "none" } + maxOutputTokens: { source: "runtime" | "runtime-snapshot" | "bundled-snapshot" | "none" } + toolCall: { source: "runtime" | "runtime-snapshot" | "bundled-snapshot" | "none" } + modalities: { source: "runtime" | "runtime-snapshot" | "bundled-snapshot" | "none" } } +const MODEL_ID_OVERRIDES: Record = {} + function isRecord(value: unknown): value is Record { return typeof value === "object" && value !== null && !Array.isArray(value) } @@ -149,20 +173,6 @@ function normalizeSnapshot(snapshot: ModelCapabilitiesSnapshot | typeof bundledM return snapshot as ModelCapabilitiesSnapshot } -function getCanonicalModelID(modelID: string): string { - const normalizedModelID = normalizeLookupModelID(modelID) - const override = MODEL_ID_OVERRIDES[normalizedModelID] - if (override?.canonicalModelID) { - return override.canonicalModelID - } - - if (normalizedModelID.startsWith("claude-") && normalizedModelID.endsWith("-thinking")) { - return normalizedModelID.replace(/-thinking$/i, "") - } - - return normalizedModelID -} - function getOverride(modelID: string): ModelCapabilityOverride | undefined { return MODEL_ID_OVERRIDES[normalizeLookupModelID(modelID)] } @@ -307,8 +317,9 @@ export function getBundledModelCapabilitiesSnapshot(): ModelCapabilitiesSnapshot } export function getModelCapabilities(input: GetModelCapabilitiesInput): ModelCapabilities { - const requestedModelID = normalizeLookupModelID(input.modelID) - const canonicalModelID = getCanonicalModelID(input.modelID) + const canonicalization = resolveModelIDAlias(input.modelID) + const requestedModelID = canonicalization.requestedModelID + const canonicalModelID = canonicalization.canonicalModelID const override = getOverride(input.modelID) const runtimeModel = readRuntimeModel( input.runtimeModel ?? findProviderModelMetadata(input.providerID, input.modelID), @@ -318,6 +329,88 @@ export function getModelCapabilities(input: GetModelCapabilitiesInput): ModelCap const snapshotEntry = runtimeSnapshot?.models?.[canonicalModelID] ?? bundledSnapshot.models[canonicalModelID] const heuristicFamily = detectHeuristicModelFamily(canonicalModelID) const runtimeVariants = readRuntimeModelVariants(runtimeModel) + const snapshotSource: ModelCapabilitiesDiagnostics["snapshot"]["source"] = + runtimeSnapshot?.models?.[canonicalModelID] + ? "runtime-snapshot" + : bundledSnapshot.models[canonicalModelID] + ? "bundled-snapshot" + : "none" + const familySource: ModelCapabilitiesDiagnostics["family"]["source"] = + snapshotEntry?.family + ? "snapshot" + : heuristicFamily?.family + ? "heuristic" + : "none" + const variantsSource: ModelCapabilitiesDiagnostics["variants"]["source"] = + runtimeVariants + ? "runtime" + : override?.variants + ? "override" + : heuristicFamily?.variants + ? "heuristic" + : "none" + const reasoningEffortsSource: ModelCapabilitiesDiagnostics["reasoningEfforts"]["source"] = + override?.reasoningEfforts + ? "override" + : heuristicFamily?.reasoningEfforts + ? "heuristic" + : "none" + const reasoningSource: ModelCapabilitiesDiagnostics["reasoning"]["source"] = + readRuntimeModelReasoningSupport(runtimeModel) !== undefined + ? "runtime" + : snapshotEntry?.reasoning !== undefined + ? snapshotSource + : "none" + const supportsThinkingSource: ModelCapabilitiesDiagnostics["supportsThinking"]["source"] = + override?.supportsThinking !== undefined + ? "override" + : heuristicFamily?.supportsThinking !== undefined + ? "heuristic" + : readRuntimeModelThinkingSupport(runtimeModel) !== undefined + ? "runtime" + : snapshotEntry?.reasoning !== undefined + ? snapshotSource + : "none" + const supportsTemperatureSource: ModelCapabilitiesDiagnostics["supportsTemperature"]["source"] = + readRuntimeModelTemperatureSupport(runtimeModel) !== undefined + ? "runtime" + : override?.supportsTemperature !== undefined + ? "override" + : snapshotEntry?.temperature !== undefined + ? snapshotSource + : "none" + const supportsTopPSource: ModelCapabilitiesDiagnostics["supportsTopP"]["source"] = + readRuntimeModelTopPSupport(runtimeModel) !== undefined + ? "runtime" + : override?.supportsTopP !== undefined + ? "override" + : "none" + const maxOutputTokensSource: ModelCapabilitiesDiagnostics["maxOutputTokens"]["source"] = + readRuntimeModelLimitOutput(runtimeModel) !== undefined + ? "runtime" + : snapshotEntry?.limit?.output !== undefined + ? snapshotSource + : "none" + const toolCallSource: ModelCapabilitiesDiagnostics["toolCall"]["source"] = + readRuntimeModelToolCallSupport(runtimeModel) !== undefined + ? "runtime" + : snapshotEntry?.toolCall !== undefined + ? snapshotSource + : "none" + const modalitiesSource: ModelCapabilitiesDiagnostics["modalities"]["source"] = + readRuntimeModelModalities(runtimeModel) !== undefined + ? "runtime" + : snapshotEntry?.modalities !== undefined + ? snapshotSource + : "none" + const resolutionMode: ModelCapabilitiesDiagnostics["resolutionMode"] = + snapshotSource !== "none" && canonicalization.source === "canonical" + ? "snapshot-backed" + : snapshotSource !== "none" + ? "alias-backed" + : familySource === "heuristic" || variantsSource === "heuristic" || reasoningEffortsSource === "heuristic" + ? "heuristic-backed" + : "unknown" return { requestedModelID, @@ -347,5 +440,23 @@ export function getModelCapabilities(input: GetModelCapabilitiesInput): ModelCap modalities: readRuntimeModelModalities(runtimeModel) ?? snapshotEntry?.modalities, + diagnostics: { + resolutionMode, + canonicalization: { + source: canonicalization.source, + ...(canonicalization.ruleID ? { ruleID: canonicalization.ruleID } : {}), + }, + snapshot: { source: snapshotSource }, + family: { source: familySource }, + variants: { source: variantsSource }, + reasoningEfforts: { source: reasoningEffortsSource }, + reasoning: { source: reasoningSource }, + supportsThinking: { source: supportsThinkingSource }, + supportsTemperature: { source: supportsTemperatureSource }, + supportsTopP: { source: supportsTopPSource }, + maxOutputTokens: { source: maxOutputTokensSource }, + toolCall: { source: toolCallSource }, + modalities: { source: modalitiesSource }, + }, } } diff --git a/src/shared/model-capability-aliases.test.ts b/src/shared/model-capability-aliases.test.ts new file mode 100644 index 000000000..31be1852a --- /dev/null +++ b/src/shared/model-capability-aliases.test.ts @@ -0,0 +1,37 @@ +import { describe, expect, test } from "bun:test" + +import { resolveModelIDAlias } from "./model-capability-aliases" + +describe("model-capability-aliases", () => { + test("keeps canonical model IDs unchanged", () => { + const result = resolveModelIDAlias("gpt-5.4") + + expect(result).toEqual({ + requestedModelID: "gpt-5.4", + canonicalModelID: "gpt-5.4", + source: "canonical", + }) + }) + + test("normalizes exact local tier aliases to canonical models.dev IDs", () => { + const result = resolveModelIDAlias("gemini-3.1-pro-high") + + expect(result).toEqual({ + requestedModelID: "gemini-3.1-pro-high", + canonicalModelID: "gemini-3.1-pro-preview", + source: "exact-alias", + ruleID: "gemini-3.1-pro-tier-alias", + }) + }) + + test("normalizes decorated thinking aliases through a named pattern rule", () => { + const result = resolveModelIDAlias("claude-opus-4-6-thinking") + + expect(result).toEqual({ + requestedModelID: "claude-opus-4-6-thinking", + canonicalModelID: "claude-opus-4-6", + source: "pattern-alias", + ruleID: "anthropic-thinking-suffix", + }) + }) +}) diff --git a/src/shared/model-capability-aliases.ts b/src/shared/model-capability-aliases.ts new file mode 100644 index 000000000..92454ad5d --- /dev/null +++ b/src/shared/model-capability-aliases.ts @@ -0,0 +1,84 @@ +type ExactAliasRule = { + ruleID: string + canonicalModelID: string +} + +type PatternAliasRule = { + ruleID: string + match: (normalizedModelID: string) => boolean + canonicalize: (normalizedModelID: string) => string +} + +export type ModelIDAliasResolution = { + requestedModelID: string + canonicalModelID: string + source: "canonical" | "exact-alias" | "pattern-alias" + ruleID?: string +} + +const EXACT_ALIAS_RULES: Record = { + "gpt-5.3-codex-spark": { + ruleID: "gpt-5.3-codex-spark-alias", + canonicalModelID: "gpt-5.3-codex", + }, + "gemini-3.1-pro-high": { + ruleID: "gemini-3.1-pro-tier-alias", + canonicalModelID: "gemini-3.1-pro-preview", + }, + "gemini-3.1-pro-low": { + ruleID: "gemini-3.1-pro-tier-alias", + canonicalModelID: "gemini-3.1-pro-preview", + }, + "gemini-3-pro-high": { + ruleID: "gemini-3-pro-tier-alias", + canonicalModelID: "gemini-3-pro-preview", + }, + "gemini-3-pro-low": { + ruleID: "gemini-3-pro-tier-alias", + canonicalModelID: "gemini-3-pro-preview", + }, +} + +const PATTERN_ALIAS_RULES: ReadonlyArray = [ + { + ruleID: "anthropic-thinking-suffix", + match: (normalizedModelID) => normalizedModelID.startsWith("claude-") && normalizedModelID.endsWith("-thinking"), + canonicalize: (normalizedModelID) => normalizedModelID.replace(/-thinking$/i, ""), + }, +] + +function normalizeLookupModelID(modelID: string): string { + return modelID.trim().toLowerCase() +} + +export function resolveModelIDAlias(modelID: string): ModelIDAliasResolution { + const normalizedModelID = normalizeLookupModelID(modelID) + const exactRule = EXACT_ALIAS_RULES[normalizedModelID] + if (exactRule) { + return { + requestedModelID: normalizedModelID, + canonicalModelID: exactRule.canonicalModelID, + source: "exact-alias", + ruleID: exactRule.ruleID, + } + } + + for (const rule of PATTERN_ALIAS_RULES) { + if (!rule.match(normalizedModelID)) { + continue + } + + return { + requestedModelID: normalizedModelID, + canonicalModelID: rule.canonicalize(normalizedModelID), + source: "pattern-alias", + ruleID: rule.ruleID, + } + } + + return { + requestedModelID: normalizedModelID, + canonicalModelID: normalizedModelID, + source: "canonical", + } +} From 55df2179b86ef9ea8ed4779fede6050dd0617c2b Mon Sep 17 00:00:00 2001 From: Ravi Tharuma Date: Wed, 25 Mar 2026 16:26:23 +0100 Subject: [PATCH 16/63] fix(todo-sync): preserve missing task priority --- src/tools/task/todo-sync.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/task/todo-sync.ts b/src/tools/task/todo-sync.ts index 1fa9f5956..c11849f8b 100644 --- a/src/tools/task/todo-sync.ts +++ b/src/tools/task/todo-sync.ts @@ -65,7 +65,7 @@ export function syncTaskToTodo(task: Task): TodoInfo | null { id: task.id, content: task.subject, status: todoStatus, - priority: extractPriority(task.metadata) ?? "medium", + priority: extractPriority(task.metadata), }; } From 5befb602298aa0597288d6ac4ab2c6fe0d49550f Mon Sep 17 00:00:00 2001 From: kuitos Date: Wed, 25 Mar 2026 23:35:40 +0800 Subject: [PATCH 17/63] feat(agent-priority): inject order field for deterministic agent Tab cycling Inject an explicit `order` field (1-4) into the four core agents (Sisyphus, Hephaestus, Prometheus, Atlas) via reorderAgentsByPriority(). This pre-empts OpenCode's alphabetical agent sorting so the intended Tab cycle order is preserved once OpenCode merges order field support (anomalyco/opencode#19127). Refs anomalyco/opencode#7372 --- .../agent-config-handler.test.ts | 1 + src/plugin-handlers/agent-priority-order.ts | 30 ++++++++++++------- 2 files changed, 21 insertions(+), 10 deletions(-) diff --git a/src/plugin-handlers/agent-config-handler.test.ts b/src/plugin-handlers/agent-config-handler.test.ts index d0d01a897..bacc3fa24 100644 --- a/src/plugin-handlers/agent-config-handler.test.ts +++ b/src/plugin-handlers/agent-config-handler.test.ts @@ -60,6 +60,7 @@ describe("applyAgentConfig builtin override protection", () => { name: "Builtin Sisyphus", prompt: "builtin prompt", mode: "primary", + order: 1, } const builtinOracleConfig: AgentConfig = { diff --git a/src/plugin-handlers/agent-priority-order.ts b/src/plugin-handlers/agent-priority-order.ts index 9ca886130..c315ad76a 100644 --- a/src/plugin-handlers/agent-priority-order.ts +++ b/src/plugin-handlers/agent-priority-order.ts @@ -1,11 +1,21 @@ import { getAgentDisplayName } from "../shared/agent-display-names"; -const CORE_AGENT_ORDER = [ - getAgentDisplayName("sisyphus"), - getAgentDisplayName("hephaestus"), - getAgentDisplayName("prometheus"), - getAgentDisplayName("atlas"), -] as const; +const CORE_AGENT_ORDER: ReadonlyArray<{ displayName: string; order: number }> = [ + { displayName: getAgentDisplayName("sisyphus"), order: 1 }, + { displayName: getAgentDisplayName("hephaestus"), order: 2 }, + { displayName: getAgentDisplayName("prometheus"), order: 3 }, + { displayName: getAgentDisplayName("atlas"), order: 4 }, +]; + +function injectOrderField( + agentConfig: unknown, + order: number, +): unknown { + if (typeof agentConfig === "object" && agentConfig !== null) { + return { ...agentConfig, order }; + } + return agentConfig; +} export function reorderAgentsByPriority( agents: Record, @@ -13,10 +23,10 @@ export function reorderAgentsByPriority( const ordered: Record = {}; const seen = new Set(); - for (const key of CORE_AGENT_ORDER) { - if (Object.prototype.hasOwnProperty.call(agents, key)) { - ordered[key] = agents[key]; - seen.add(key); + for (const { displayName, order } of CORE_AGENT_ORDER) { + if (Object.prototype.hasOwnProperty.call(agents, displayName)) { + ordered[displayName] = injectOrderField(agents[displayName], order); + seen.add(displayName); } } From 46c6e1dcf66d989059d486ff96df0cc755d8e057 Mon Sep 17 00:00:00 2001 From: Ravi Tharuma Date: Wed, 25 Mar 2026 16:38:21 +0100 Subject: [PATCH 18/63] test(todo-sync): match required priority fallback --- src/tools/task/todo-sync.test.ts | 6 +++--- src/tools/task/todo-sync.ts | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/tools/task/todo-sync.test.ts b/src/tools/task/todo-sync.test.ts index e35d1978b..d6c87c3df 100644 --- a/src/tools/task/todo-sync.test.ts +++ b/src/tools/task/todo-sync.test.ts @@ -27,7 +27,7 @@ describe("syncTaskToTodo", () => { id: "T-123", content: "Fix bug", status: "pending", - priority: undefined, + priority: "medium", }); }); @@ -159,7 +159,7 @@ describe("syncTaskToTodo", () => { const result = syncTaskToTodo(task); // then - expect(result?.priority).toBeUndefined(); + expect(result?.priority).toBe("medium"); }); it("handles missing metadata", () => { @@ -177,7 +177,7 @@ describe("syncTaskToTodo", () => { const result = syncTaskToTodo(task); // then - expect(result?.priority).toBeUndefined(); + expect(result?.priority).toBe("medium"); }); it("uses subject as todo content", () => { diff --git a/src/tools/task/todo-sync.ts b/src/tools/task/todo-sync.ts index c11849f8b..1fa9f5956 100644 --- a/src/tools/task/todo-sync.ts +++ b/src/tools/task/todo-sync.ts @@ -65,7 +65,7 @@ export function syncTaskToTodo(task: Task): TodoInfo | null { id: task.id, content: task.subject, status: todoStatus, - priority: extractPriority(task.metadata), + priority: extractPriority(task.metadata) ?? "medium", }; } From 8df3a2876a54cbe3643b80bb96f44d6600a59a4c Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 25 Mar 2026 18:48:32 +0000 Subject: [PATCH 19/63] @anas-asghar4831 has signed the CLA in code-yeongyu/oh-my-openagent#2837 --- signatures/cla.json | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/signatures/cla.json b/signatures/cla.json index 46ed01db9..95fd098fd 100644 --- a/signatures/cla.json +++ b/signatures/cla.json @@ -2303,6 +2303,14 @@ "created_at": "2026-03-23T04:28:20Z", "repoId": 1108837393, "pullRequestNo": 2758 + }, + { + "name": "anas-asghar4831", + "id": 110368394, + "comment_id": 4128950310, + "created_at": "2026-03-25T18:48:19Z", + "repoId": 1108837393, + "pullRequestNo": 2837 } ] } \ No newline at end of file From 5043cc21ac3ab5a0ee4869673b1545573f2c57e4 Mon Sep 17 00:00:00 2001 From: Ravi Tharuma Date: Wed, 25 Mar 2026 17:13:43 +0100 Subject: [PATCH 20/63] fix(model-capabilities): harden canonical alias guardrails --- .../workflows/refresh-model-capabilities.yml | 3 + docs/model-capabilities-maintenance.md | 33 ++++ package.json | 1 + src/shared/model-capabilities.test.ts | 6 +- src/shared/model-capability-aliases.test.ts | 6 +- src/shared/model-capability-aliases.ts | 79 ++++++---- .../model-capability-guardrails.test.ts | 92 +++++++++++ src/shared/model-capability-guardrails.ts | 149 ++++++++++++++++++ 8 files changed, 333 insertions(+), 36 deletions(-) create mode 100644 docs/model-capabilities-maintenance.md create mode 100644 src/shared/model-capability-guardrails.test.ts create mode 100644 src/shared/model-capability-guardrails.ts diff --git a/.github/workflows/refresh-model-capabilities.yml b/.github/workflows/refresh-model-capabilities.yml index 5d2d053fa..dd34e43ed 100644 --- a/.github/workflows/refresh-model-capabilities.yml +++ b/.github/workflows/refresh-model-capabilities.yml @@ -28,6 +28,9 @@ jobs: - name: Refresh bundled model capabilities snapshot run: bun run build:model-capabilities + - name: Validate capability guardrails + run: bun run test:model-capabilities + - name: Create refresh pull request uses: peter-evans/create-pull-request@v7 with: diff --git a/docs/model-capabilities-maintenance.md b/docs/model-capabilities-maintenance.md new file mode 100644 index 000000000..4f6d6bbce --- /dev/null +++ b/docs/model-capabilities-maintenance.md @@ -0,0 +1,33 @@ +# Model Capabilities Maintenance + +This project treats model capability resolution as a layered system: + +1. runtime metadata from connected providers +2. `models.dev` bundled/runtime snapshot data +3. explicit compatibility aliases +4. heuristic fallback as the last resort + +## Internal policy + +- Built-in OmO agent/category requirement models must use canonical model IDs. +- Aliases exist only to preserve compatibility with historical OmO names or provider-specific decorations. +- New decorated names like `-high`, `-low`, or `-thinking` should not be added to built-in requirements when a canonical model ID plus structured settings can express the same thing. +- If a provider or config input still uses an alias, normalize it at the edge and continue internally with the canonical ID. + +## When adding an alias + +- Add the alias rule to `src/shared/model-capability-aliases.ts`. +- Include a rationale for why the alias exists. +- Add or update tests so the alias is covered explicitly. +- Ensure the alias canonical target exists in the bundled `models.dev` snapshot. + +## Guardrails + +`bun run test:model-capabilities` enforces the following invariants: + +- exact alias targets must exist in the bundled snapshot +- exact alias keys must not silently become canonical `models.dev` IDs +- pattern aliases must not rewrite canonical snapshot IDs +- built-in requirement models must stay canonical and snapshot-backed + +The scheduled `refresh-model-capabilities` workflow runs these guardrails before opening an automated snapshot refresh PR. diff --git a/package.json b/package.json index 1b496f000..2c4a4e857 100644 --- a/package.json +++ b/package.json @@ -30,6 +30,7 @@ "prepare": "bun run build", "postinstall": "node postinstall.mjs", "prepublishOnly": "bun run clean && bun run build", + "test:model-capabilities": "bun test src/shared/model-capability-aliases.test.ts src/shared/model-capability-guardrails.test.ts src/shared/model-capabilities.test.ts src/cli/doctor/checks/model-resolution.test.ts --bail", "typecheck": "tsc --noEmit", "test": "bun test" }, diff --git a/src/shared/model-capabilities.test.ts b/src/shared/model-capabilities.test.ts index afad4ba0a..4a97002f9 100644 --- a/src/shared/model-capabilities.test.ts +++ b/src/shared/model-capabilities.test.ts @@ -161,7 +161,7 @@ describe("getModelCapabilities", () => { expect(result.variants).toEqual(["low", "medium", "high", "xhigh"]) }) - test("normalizes thinking suffix aliases before snapshot lookup", () => { + test("normalizes the legacy Claude Opus thinking alias before snapshot lookup", () => { const result = getModelCapabilities({ providerID: "anthropic", modelID: "claude-opus-4-6-thinking", @@ -178,8 +178,8 @@ describe("getModelCapabilities", () => { expect(result.diagnostics).toMatchObject({ resolutionMode: "alias-backed", canonicalization: { - source: "pattern-alias", - ruleID: "anthropic-thinking-suffix", + source: "exact-alias", + ruleID: "claude-opus-4-6-thinking-legacy-alias", }, snapshot: { source: "bundled-snapshot" }, }) diff --git a/src/shared/model-capability-aliases.test.ts b/src/shared/model-capability-aliases.test.ts index 31be1852a..a164ffb60 100644 --- a/src/shared/model-capability-aliases.test.ts +++ b/src/shared/model-capability-aliases.test.ts @@ -24,14 +24,14 @@ describe("model-capability-aliases", () => { }) }) - test("normalizes decorated thinking aliases through a named pattern rule", () => { + test("normalizes legacy Claude thinking aliases through a named exact rule", () => { const result = resolveModelIDAlias("claude-opus-4-6-thinking") expect(result).toEqual({ requestedModelID: "claude-opus-4-6-thinking", canonicalModelID: "claude-opus-4-6", - source: "pattern-alias", - ruleID: "anthropic-thinking-suffix", + source: "exact-alias", + ruleID: "claude-opus-4-6-thinking-legacy-alias", }) }) }) diff --git a/src/shared/model-capability-aliases.ts b/src/shared/model-capability-aliases.ts index 92454ad5d..523755d61 100644 --- a/src/shared/model-capability-aliases.ts +++ b/src/shared/model-capability-aliases.ts @@ -1,10 +1,13 @@ -type ExactAliasRule = { +export type ExactAliasRule = { + aliasModelID: string ruleID: string canonicalModelID: string + rationale: string } -type PatternAliasRule = { +export type PatternAliasRule = { ruleID: string + description: string match: (normalizedModelID: string) => boolean canonicalize: (normalizedModelID: string) => string } @@ -16,44 +19,52 @@ export type ModelIDAliasResolution = { ruleID?: string } -const EXACT_ALIAS_RULES: Record = { - "gpt-5.3-codex-spark": { - ruleID: "gpt-5.3-codex-spark-alias", - canonicalModelID: "gpt-5.3-codex", - }, - "gemini-3.1-pro-high": { - ruleID: "gemini-3.1-pro-tier-alias", - canonicalModelID: "gemini-3.1-pro-preview", - }, - "gemini-3.1-pro-low": { - ruleID: "gemini-3.1-pro-tier-alias", - canonicalModelID: "gemini-3.1-pro-preview", - }, - "gemini-3-pro-high": { - ruleID: "gemini-3-pro-tier-alias", - canonicalModelID: "gemini-3-pro-preview", - }, - "gemini-3-pro-low": { - ruleID: "gemini-3-pro-tier-alias", - canonicalModelID: "gemini-3-pro-preview", - }, -} - -const PATTERN_ALIAS_RULES: ReadonlyArray = [ +const EXACT_ALIAS_RULES: ReadonlyArray = [ { - ruleID: "anthropic-thinking-suffix", - match: (normalizedModelID) => normalizedModelID.startsWith("claude-") && normalizedModelID.endsWith("-thinking"), - canonicalize: (normalizedModelID) => normalizedModelID.replace(/-thinking$/i, ""), + aliasModelID: "gemini-3.1-pro-high", + ruleID: "gemini-3.1-pro-tier-alias", + canonicalModelID: "gemini-3.1-pro-preview", + rationale: "OmO historically encoded Gemini tier selection in the model name instead of variant metadata.", + }, + { + aliasModelID: "gemini-3.1-pro-low", + ruleID: "gemini-3.1-pro-tier-alias", + canonicalModelID: "gemini-3.1-pro-preview", + rationale: "OmO historically encoded Gemini tier selection in the model name instead of variant metadata.", + }, + { + aliasModelID: "gemini-3-pro-high", + ruleID: "gemini-3-pro-tier-alias", + canonicalModelID: "gemini-3-pro-preview", + rationale: "Legacy Gemini 3 tier suffixes still need to land on the canonical preview model.", + }, + { + aliasModelID: "gemini-3-pro-low", + ruleID: "gemini-3-pro-tier-alias", + canonicalModelID: "gemini-3-pro-preview", + rationale: "Legacy Gemini 3 tier suffixes still need to land on the canonical preview model.", + }, + { + aliasModelID: "claude-opus-4-6-thinking", + ruleID: "claude-opus-4-6-thinking-legacy-alias", + canonicalModelID: "claude-opus-4-6", + rationale: "OmO historically used a legacy compatibility suffix before models.dev shipped canonical thinking variants for newer Claude families.", }, ] +const EXACT_ALIAS_RULES_BY_MODEL: Readonly> = Object.fromEntries( + EXACT_ALIAS_RULES.map((rule) => [rule.aliasModelID, rule]), +) + +const PATTERN_ALIAS_RULES: ReadonlyArray = [] + function normalizeLookupModelID(modelID: string): string { return modelID.trim().toLowerCase() } export function resolveModelIDAlias(modelID: string): ModelIDAliasResolution { const normalizedModelID = normalizeLookupModelID(modelID) - const exactRule = EXACT_ALIAS_RULES[normalizedModelID] + const exactRule = EXACT_ALIAS_RULES_BY_MODEL[normalizedModelID] if (exactRule) { return { requestedModelID: normalizedModelID, @@ -82,3 +93,11 @@ export function resolveModelIDAlias(modelID: string): ModelIDAliasResolution { source: "canonical", } } + +export function getExactModelIDAliasRules(): ReadonlyArray { + return EXACT_ALIAS_RULES +} + +export function getPatternModelIDAliasRules(): ReadonlyArray { + return PATTERN_ALIAS_RULES +} diff --git a/src/shared/model-capability-guardrails.test.ts b/src/shared/model-capability-guardrails.test.ts new file mode 100644 index 000000000..c5e7c54fc --- /dev/null +++ b/src/shared/model-capability-guardrails.test.ts @@ -0,0 +1,92 @@ +import { describe, expect, test } from "bun:test" + +import type { ModelCapabilitiesSnapshot } from "./model-capabilities" +import { getBundledModelCapabilitiesSnapshot } from "./model-capabilities" +import { + collectModelCapabilityGuardrailIssues, + getBuiltInRequirementModelIDs, +} from "./model-capability-guardrails" + +describe("model-capability-guardrails", () => { + test("keeps the current alias registry and built-in requirements aligned with the bundled snapshot", () => { + const issues = collectModelCapabilityGuardrailIssues() + + expect(issues).toEqual([]) + }) + + test("requires built-in requirement models to stay unique and sorted", () => { + const modelIDs = getBuiltInRequirementModelIDs() + + expect(modelIDs).toEqual([...modelIDs].sort()) + expect(new Set(modelIDs).size).toBe(modelIDs.length) + expect(modelIDs).toContain("claude-opus-4-6") + expect(modelIDs).toContain("gpt-5.4") + expect(modelIDs).toContain("kimi-k2.5") + }) + + test("flags exact aliases whose canonical target disappears from the snapshot", () => { + const bundledSnapshot = getBundledModelCapabilitiesSnapshot() + const brokenSnapshot: ModelCapabilitiesSnapshot = { + ...bundledSnapshot, + models: Object.fromEntries( + Object.entries(bundledSnapshot.models).filter(([modelID]) => modelID !== "gemini-3.1-pro-preview"), + ), + } + + const issues = collectModelCapabilityGuardrailIssues({ + snapshot: brokenSnapshot, + requirementModelIDs: [], + }) + + expect(issues).toContainEqual( + expect.objectContaining({ + kind: "alias-target-missing-from-snapshot", + aliasModelID: "gemini-3.1-pro-high", + canonicalModelID: "gemini-3.1-pro-preview", + }), + ) + }) + + test("flags exact aliases when models.dev gains a canonical entry for the alias itself", () => { + const bundledSnapshot = getBundledModelCapabilitiesSnapshot() + const aliasCollisionSnapshot: ModelCapabilitiesSnapshot = { + ...bundledSnapshot, + models: { + ...bundledSnapshot.models, + "gemini-3.1-pro-high": { + id: "gemini-3.1-pro-high", + family: "gemini", + reasoning: true, + }, + }, + } + + const issues = collectModelCapabilityGuardrailIssues({ + snapshot: aliasCollisionSnapshot, + requirementModelIDs: [], + }) + + expect(issues).toContainEqual( + expect.objectContaining({ + kind: "exact-alias-collides-with-snapshot", + aliasModelID: "gemini-3.1-pro-high", + canonicalModelID: "gemini-3.1-pro-preview", + }), + ) + }) + + test("flags built-in requirement models that rely on aliases instead of canonical IDs", () => { + const issues = collectModelCapabilityGuardrailIssues({ + requirementModelIDs: ["gemini-3.1-pro-high"], + }) + + expect(issues).toContainEqual( + expect.objectContaining({ + kind: "built-in-model-relies-on-alias", + modelID: "gemini-3.1-pro-high", + canonicalModelID: "gemini-3.1-pro-preview", + ruleID: "gemini-3.1-pro-tier-alias", + }), + ) + }) +}) diff --git a/src/shared/model-capability-guardrails.ts b/src/shared/model-capability-guardrails.ts new file mode 100644 index 000000000..b1c74feae --- /dev/null +++ b/src/shared/model-capability-guardrails.ts @@ -0,0 +1,149 @@ +import type { ModelCapabilitiesSnapshot } from "./model-capabilities" +import { getBundledModelCapabilitiesSnapshot } from "./model-capabilities" +import { + getExactModelIDAliasRules, + getPatternModelIDAliasRules, + resolveModelIDAlias, +} from "./model-capability-aliases" +import { AGENT_MODEL_REQUIREMENTS, CATEGORY_MODEL_REQUIREMENTS } from "./model-requirements" + +export type ModelCapabilityGuardrailIssue = + | { + kind: "alias-target-missing-from-snapshot" + ruleID: string + aliasModelID: string + canonicalModelID: string + message: string + } + | { + kind: "exact-alias-collides-with-snapshot" + ruleID: string + aliasModelID: string + canonicalModelID: string + message: string + } + | { + kind: "pattern-alias-collides-with-snapshot" + ruleID: string + modelID: string + canonicalModelID: string + message: string + } + | { + kind: "built-in-model-relies-on-alias" + modelID: string + canonicalModelID: string + ruleID: string + message: string + } + | { + kind: "built-in-model-missing-from-snapshot" + modelID: string + canonicalModelID: string + message: string + } + +type CollectModelCapabilityGuardrailIssuesInput = { + snapshot?: ModelCapabilitiesSnapshot + requirementModelIDs?: Iterable +} + +function normalizeLookupModelID(modelID: string): string { + return modelID.trim().toLowerCase() +} + +export function getBuiltInRequirementModelIDs(): string[] { + const modelIDs = new Set() + + for (const requirement of Object.values(AGENT_MODEL_REQUIREMENTS)) { + for (const entry of requirement.fallbackChain) { + modelIDs.add(entry.model) + } + } + + for (const requirement of Object.values(CATEGORY_MODEL_REQUIREMENTS)) { + for (const entry of requirement.fallbackChain) { + modelIDs.add(entry.model) + } + } + + return [...modelIDs].sort() +} + +export function collectModelCapabilityGuardrailIssues( + input: CollectModelCapabilityGuardrailIssuesInput = {}, +): ModelCapabilityGuardrailIssue[] { + const snapshot = input.snapshot ?? getBundledModelCapabilitiesSnapshot() + const snapshotModelIDs = new Set( + Object.keys(snapshot.models).map((modelID) => normalizeLookupModelID(modelID)), + ) + const requirementModelIDs = input.requirementModelIDs ?? getBuiltInRequirementModelIDs() + const issues: ModelCapabilityGuardrailIssue[] = [] + + for (const rule of getExactModelIDAliasRules()) { + if (!snapshotModelIDs.has(rule.canonicalModelID)) { + issues.push({ + kind: "alias-target-missing-from-snapshot", + ruleID: rule.ruleID, + aliasModelID: rule.aliasModelID, + canonicalModelID: rule.canonicalModelID, + message: `Alias ${rule.aliasModelID} points to missing snapshot model ${rule.canonicalModelID}.`, + }) + } + + if (snapshotModelIDs.has(rule.aliasModelID)) { + issues.push({ + kind: "exact-alias-collides-with-snapshot", + ruleID: rule.ruleID, + aliasModelID: rule.aliasModelID, + canonicalModelID: rule.canonicalModelID, + message: `Alias ${rule.aliasModelID} now exists in models.dev and should be reviewed instead of force-mapping to ${rule.canonicalModelID}.`, + }) + } + } + + for (const rule of getPatternModelIDAliasRules()) { + for (const modelID of snapshotModelIDs) { + if (!rule.match(modelID)) { + continue + } + + const canonicalModelID = rule.canonicalize(modelID) + if (canonicalModelID === modelID) { + continue + } + + issues.push({ + kind: "pattern-alias-collides-with-snapshot", + ruleID: rule.ruleID, + modelID, + canonicalModelID, + message: `Pattern alias ${rule.ruleID} would rewrite canonical snapshot model ${modelID} to ${canonicalModelID}.`, + }) + } + } + + for (const modelID of requirementModelIDs) { + const aliasResolution = resolveModelIDAlias(modelID) + if (aliasResolution.source !== "canonical") { + issues.push({ + kind: "built-in-model-relies-on-alias", + modelID: aliasResolution.requestedModelID, + canonicalModelID: aliasResolution.canonicalModelID, + ruleID: aliasResolution.ruleID ?? "unknown-alias-rule", + message: `Built-in requirement model ${aliasResolution.requestedModelID} should be canonical and not rely on alias rule ${aliasResolution.ruleID}.`, + }) + } + + if (!snapshotModelIDs.has(aliasResolution.canonicalModelID)) { + issues.push({ + kind: "built-in-model-missing-from-snapshot", + modelID: aliasResolution.requestedModelID, + canonicalModelID: aliasResolution.canonicalModelID, + message: `Built-in requirement model ${aliasResolution.requestedModelID} resolves to ${aliasResolution.canonicalModelID}, which is missing from the bundled snapshot.`, + }) + } + } + + return issues +} From ec20a82b4eb68813429137e066247dffff2ab2d5 Mon Sep 17 00:00:00 2001 From: Ravi Tharuma Date: Wed, 25 Mar 2026 22:19:51 +0100 Subject: [PATCH 21/63] fix(model-capabilities): align gemini aliases and alias lookup --- src/shared/model-capabilities.test.ts | 6 +++--- src/shared/model-capability-aliases.test.ts | 12 +++++++++++- src/shared/model-capability-aliases.ts | 8 ++++---- src/shared/model-capability-guardrails.test.ts | 8 ++++---- 4 files changed, 22 insertions(+), 12 deletions(-) diff --git a/src/shared/model-capabilities.test.ts b/src/shared/model-capabilities.test.ts index 4a97002f9..35dc40f8b 100644 --- a/src/shared/model-capabilities.test.ts +++ b/src/shared/model-capabilities.test.ts @@ -27,8 +27,8 @@ describe("getModelCapabilities", () => { }, toolCall: true, }, - "gemini-3.1-pro-preview": { - id: "gemini-3.1-pro-preview", + "gemini-3.1-pro": { + id: "gemini-3.1-pro", family: "gemini", reasoning: true, temperature: true, @@ -193,7 +193,7 @@ describe("getModelCapabilities", () => { }) expect(result).toMatchObject({ - canonicalModelID: "gemini-3.1-pro-preview", + canonicalModelID: "gemini-3.1-pro", family: "gemini", supportsThinking: true, supportsTemperature: true, diff --git a/src/shared/model-capability-aliases.test.ts b/src/shared/model-capability-aliases.test.ts index a164ffb60..9e563fc02 100644 --- a/src/shared/model-capability-aliases.test.ts +++ b/src/shared/model-capability-aliases.test.ts @@ -18,12 +18,22 @@ describe("model-capability-aliases", () => { expect(result).toEqual({ requestedModelID: "gemini-3.1-pro-high", - canonicalModelID: "gemini-3.1-pro-preview", + canonicalModelID: "gemini-3.1-pro", source: "exact-alias", ruleID: "gemini-3.1-pro-tier-alias", }) }) + test("does not resolve prototype keys as aliases", () => { + const result = resolveModelIDAlias("constructor") + + expect(result).toEqual({ + requestedModelID: "constructor", + canonicalModelID: "constructor", + source: "canonical", + }) + }) + test("normalizes legacy Claude thinking aliases through a named exact rule", () => { const result = resolveModelIDAlias("claude-opus-4-6-thinking") diff --git a/src/shared/model-capability-aliases.ts b/src/shared/model-capability-aliases.ts index 523755d61..953b5a300 100644 --- a/src/shared/model-capability-aliases.ts +++ b/src/shared/model-capability-aliases.ts @@ -23,13 +23,13 @@ const EXACT_ALIAS_RULES: ReadonlyArray = [ { aliasModelID: "gemini-3.1-pro-high", ruleID: "gemini-3.1-pro-tier-alias", - canonicalModelID: "gemini-3.1-pro-preview", + canonicalModelID: "gemini-3.1-pro", rationale: "OmO historically encoded Gemini tier selection in the model name instead of variant metadata.", }, { aliasModelID: "gemini-3.1-pro-low", ruleID: "gemini-3.1-pro-tier-alias", - canonicalModelID: "gemini-3.1-pro-preview", + canonicalModelID: "gemini-3.1-pro", rationale: "OmO historically encoded Gemini tier selection in the model name instead of variant metadata.", }, { @@ -52,7 +52,7 @@ const EXACT_ALIAS_RULES: ReadonlyArray = [ }, ] -const EXACT_ALIAS_RULES_BY_MODEL: Readonly> = Object.fromEntries( +const EXACT_ALIAS_RULES_BY_MODEL: ReadonlyMap = new Map( EXACT_ALIAS_RULES.map((rule) => [rule.aliasModelID, rule]), ) @@ -64,7 +64,7 @@ function normalizeLookupModelID(modelID: string): string { export function resolveModelIDAlias(modelID: string): ModelIDAliasResolution { const normalizedModelID = normalizeLookupModelID(modelID) - const exactRule = EXACT_ALIAS_RULES_BY_MODEL[normalizedModelID] + const exactRule = EXACT_ALIAS_RULES_BY_MODEL.get(normalizedModelID) if (exactRule) { return { requestedModelID: normalizedModelID, diff --git a/src/shared/model-capability-guardrails.test.ts b/src/shared/model-capability-guardrails.test.ts index c5e7c54fc..06a9c07eb 100644 --- a/src/shared/model-capability-guardrails.test.ts +++ b/src/shared/model-capability-guardrails.test.ts @@ -29,7 +29,7 @@ describe("model-capability-guardrails", () => { const brokenSnapshot: ModelCapabilitiesSnapshot = { ...bundledSnapshot, models: Object.fromEntries( - Object.entries(bundledSnapshot.models).filter(([modelID]) => modelID !== "gemini-3.1-pro-preview"), + Object.entries(bundledSnapshot.models).filter(([modelID]) => modelID !== "gemini-3.1-pro"), ), } @@ -42,7 +42,7 @@ describe("model-capability-guardrails", () => { expect.objectContaining({ kind: "alias-target-missing-from-snapshot", aliasModelID: "gemini-3.1-pro-high", - canonicalModelID: "gemini-3.1-pro-preview", + canonicalModelID: "gemini-3.1-pro", }), ) }) @@ -70,7 +70,7 @@ describe("model-capability-guardrails", () => { expect.objectContaining({ kind: "exact-alias-collides-with-snapshot", aliasModelID: "gemini-3.1-pro-high", - canonicalModelID: "gemini-3.1-pro-preview", + canonicalModelID: "gemini-3.1-pro", }), ) }) @@ -84,7 +84,7 @@ describe("model-capability-guardrails", () => { expect.objectContaining({ kind: "built-in-model-relies-on-alias", modelID: "gemini-3.1-pro-high", - canonicalModelID: "gemini-3.1-pro-preview", + canonicalModelID: "gemini-3.1-pro", ruleID: "gemini-3.1-pro-tier-alias", }), ) From ce877ec0d8f42e0667bb01dbec88bdfc889d5458 Mon Sep 17 00:00:00 2001 From: Ravi Tharuma Date: Wed, 25 Mar 2026 22:27:26 +0100 Subject: [PATCH 22/63] test(atlas): avoid shared barrel mock pollution --- src/hooks/atlas/session-last-agent.sqlite.test.ts | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/hooks/atlas/session-last-agent.sqlite.test.ts b/src/hooks/atlas/session-last-agent.sqlite.test.ts index 8501223b6..036482db5 100644 --- a/src/hooks/atlas/session-last-agent.sqlite.test.ts +++ b/src/hooks/atlas/session-last-agent.sqlite.test.ts @@ -1,8 +1,14 @@ const { describe, expect, mock, test } = require("bun:test") -mock.module("../../shared", () => ({ +mock.module("../../shared/opencode-message-dir", () => ({ getMessageDir: () => null, +})) + +mock.module("../../shared/opencode-storage-detection", () => ({ isSqliteBackend: () => true, +})) + +mock.module("../../shared/normalize-sdk-response", () => ({ normalizeSDKResponse: (response: { data?: TData }, fallback: TData): TData => response.data ?? fallback, })) From 4d4680be3c1cfff32c68834431dba9ee94da86a6 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 25 Mar 2026 21:33:49 +0000 Subject: [PATCH 23/63] @clansty has signed the CLA in code-yeongyu/oh-my-openagent#2839 --- signatures/cla.json | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/signatures/cla.json b/signatures/cla.json index 95fd098fd..21f4dc2a2 100644 --- a/signatures/cla.json +++ b/signatures/cla.json @@ -2311,6 +2311,14 @@ "created_at": "2026-03-25T18:48:19Z", "repoId": 1108837393, "pullRequestNo": 2837 + }, + { + "name": "clansty", + "id": 18461360, + "comment_id": 4129934858, + "created_at": "2026-03-25T21:33:35Z", + "repoId": 1108837393, + "pullRequestNo": 2839 } ] } \ No newline at end of file From ce1bffbc4dace3e3b4063d4b34ada7c968fb1917 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 25 Mar 2026 23:11:43 +0000 Subject: [PATCH 24/63] @ventsislav-georgiev has signed the CLA in code-yeongyu/oh-my-openagent#2840 --- signatures/cla.json | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/signatures/cla.json b/signatures/cla.json index 21f4dc2a2..54511aade 100644 --- a/signatures/cla.json +++ b/signatures/cla.json @@ -2319,6 +2319,14 @@ "created_at": "2026-03-25T21:33:35Z", "repoId": 1108837393, "pullRequestNo": 2839 + }, + { + "name": "ventsislav-georgiev", + "id": 5616486, + "comment_id": 4130417794, + "created_at": "2026-03-25T23:11:32Z", + "repoId": 1108837393, + "pullRequestNo": 2840 } ] } \ No newline at end of file From 7895361f4299648f92c1b2c46543e560f26fbef2 Mon Sep 17 00:00:00 2001 From: YeonGyu-Kim Date: Thu, 26 Mar 2026 09:30:34 +0900 Subject: [PATCH 25/63] fix(tests): resolve 5 cross-file test isolation failures - model-fallback hook: mock selectFallbackProvider and add _resetForTesting() to test-setup.ts to clear module-level state between files - fallback-retry-handler: add afterAll(mock.restore) and use mockReturnValueOnce to prevent connected-providers mock leaking to subsequent test files - opencode-config-dir: use win32.join for Windows APPDATA path construction so tests pass on macOS (path.join uses POSIX semantics regardless of process.platform override) - system-loaded-version: use resolveSymlink from file-utils instead of realpathSync to handle macOS /var -> /private/var symlink consistently All 4456 tests pass (0 failures) on full bun test suite. --- .../checks/system-loaded-version.test.ts | 5 ++-- .../doctor/checks/system-loaded-version.ts | 11 +++------ .../fallback-retry-handler.test.ts | 10 +++++--- src/hooks/model-fallback/hook.test.ts | 23 +++++++++++++++++++ src/hooks/model-fallback/hook.ts | 10 ++++++++ src/shared/opencode-config-dir.test.ts | 7 +++--- src/shared/opencode-config-dir.ts | 9 +++++--- test-setup.ts | 6 +++-- 8 files changed, 60 insertions(+), 21 deletions(-) diff --git a/src/cli/doctor/checks/system-loaded-version.test.ts b/src/cli/doctor/checks/system-loaded-version.test.ts index b35e5a638..de4c9391f 100644 --- a/src/cli/doctor/checks/system-loaded-version.test.ts +++ b/src/cli/doctor/checks/system-loaded-version.test.ts @@ -1,9 +1,10 @@ import { afterEach, describe, expect, it } from "bun:test" -import { mkdirSync, mkdtempSync, realpathSync, rmSync, symlinkSync, writeFileSync } from "node:fs" +import { mkdirSync, mkdtempSync, rmSync, symlinkSync, writeFileSync } from "node:fs" import { tmpdir } from "node:os" import { dirname, join } from "node:path" import { PACKAGE_NAME } from "../constants" +import { resolveSymlink } from "../../../shared/file-utils" const systemLoadedVersionModulePath = "./system-loaded-version?system-loaded-version-test" @@ -125,7 +126,7 @@ describe("system loaded version", () => { const loadedVersion = getLoadedPluginVersion() //#then - expect(loadedVersion.cacheDir).toBe(realpathSync(symlinkConfigDir)) + expect(loadedVersion.cacheDir).toBe(resolveSymlink(symlinkConfigDir)) expect(loadedVersion.expectedVersion).toBe("4.5.6") expect(loadedVersion.loadedVersion).toBe("4.5.6") }) diff --git a/src/cli/doctor/checks/system-loaded-version.ts b/src/cli/doctor/checks/system-loaded-version.ts index 7693b2d7a..04e4a87d1 100644 --- a/src/cli/doctor/checks/system-loaded-version.ts +++ b/src/cli/doctor/checks/system-loaded-version.ts @@ -1,7 +1,7 @@ -import { existsSync, readFileSync, realpathSync } from "node:fs" +import { existsSync, readFileSync } from "node:fs" import { homedir } from "node:os" import { join } from "node:path" - +import { resolveSymlink } from "../../../shared/file-utils" import { getLatestVersion } from "../../../hooks/auto-update-checker/checker" import { extractChannel } from "../../../hooks/auto-update-checker" import { PACKAGE_NAME } from "../constants" @@ -38,12 +38,7 @@ function resolveOpenCodeCacheDir(): string { function resolveExistingDir(dirPath: string): string { if (!existsSync(dirPath)) return dirPath - - try { - return realpathSync(dirPath) - } catch { - return dirPath - } + return resolveSymlink(dirPath) } function readPackageJson(filePath: string): PackageJsonShape | null { diff --git a/src/features/background-agent/fallback-retry-handler.test.ts b/src/features/background-agent/fallback-retry-handler.test.ts index 03cd2b16f..825f72a56 100644 --- a/src/features/background-agent/fallback-retry-handler.test.ts +++ b/src/features/background-agent/fallback-retry-handler.test.ts @@ -1,4 +1,4 @@ -import { describe, test, expect, mock, beforeEach } from "bun:test" +import { afterAll, beforeEach, describe, expect, mock, test } from "bun:test" mock.module("../../shared", () => ({ log: mock(() => {}), @@ -82,6 +82,10 @@ function createDefaultArgs(taskOverrides: Partial = {}) { } describe("tryFallbackRetry", () => { + afterAll(() => { + mock.restore() + }) + beforeEach(() => { ;(shouldRetryError as any).mockImplementation(() => true) ;(selectFallbackProvider as any).mockImplementation((providers: string[]) => providers[0]) @@ -274,8 +278,8 @@ describe("tryFallbackRetry", () => { describe("#given disconnected fallback providers with connected preferred provider", () => { test("keeps fallback entry and selects connected preferred provider", () => { - ;(readProviderModelsCache as any).mockReturnValue({ connected: ["provider-a"] }) - ;(selectFallbackProvider as any).mockImplementation( + ;(readProviderModelsCache as any).mockReturnValueOnce({ connected: ["provider-a"] }) + ;(selectFallbackProvider as any).mockImplementationOnce( (_providers: string[], preferredProviderID?: string) => preferredProviderID ?? "provider-b", ) diff --git a/src/hooks/model-fallback/hook.test.ts b/src/hooks/model-fallback/hook.test.ts index 92f630906..09757ab3f 100644 --- a/src/hooks/model-fallback/hook.test.ts +++ b/src/hooks/model-fallback/hook.test.ts @@ -3,6 +3,24 @@ const { beforeEach, describe, expect, mock, test } = require("bun:test") const readConnectedProvidersCacheMock = mock(() => null) const readProviderModelsCacheMock = mock(() => null) +const selectFallbackProviderMock = mock((providers: string[], preferredProviderID?: string) => { + const connectedProviders = readConnectedProvidersCacheMock() + if (connectedProviders) { + const connectedSet = new Set(connectedProviders.map((provider: string) => provider.toLowerCase())) + + for (const provider of providers) { + if (connectedSet.has(provider.toLowerCase())) { + return provider + } + } + + if (preferredProviderID && connectedSet.has(preferredProviderID.toLowerCase())) { + return preferredProviderID + } + } + + return providers[0] || preferredProviderID || "opencode" +}) const transformModelForProviderMock = mock((provider: string, model: string) => { if (provider === "github-copilot") { return model @@ -31,6 +49,10 @@ mock.module("../../shared/provider-model-id-transform", () => ({ transformModelForProvider: transformModelForProviderMock, })) +mock.module("../../shared/model-error-classifier", () => ({ + selectFallbackProvider: selectFallbackProviderMock, +})) + import { clearPendingModelFallback, createModelFallbackHook, @@ -44,6 +66,7 @@ describe("model fallback hook", () => { readProviderModelsCacheMock.mockReturnValue(null) readConnectedProvidersCacheMock.mockClear() readProviderModelsCacheMock.mockClear() + selectFallbackProviderMock.mockClear() clearPendingModelFallback("ses_model_fallback_main") clearPendingModelFallback("ses_model_fallback_ghcp") diff --git a/src/hooks/model-fallback/hook.ts b/src/hooks/model-fallback/hook.ts index 045bba2df..cbbcbc935 100644 --- a/src/hooks/model-fallback/hook.ts +++ b/src/hooks/model-fallback/hook.ts @@ -274,3 +274,13 @@ export function createModelFallbackHook(args?: { toast?: FallbackToast; onApplie }, } } + +/** + * Resets all module-global state for testing. + * Clears pending fallbacks, toast keys, and session chains. + */ +export function _resetForTesting(): void { + pendingModelFallbacks.clear() + lastToastKey.clear() + sessionFallbackChains.clear() +} diff --git a/src/shared/opencode-config-dir.test.ts b/src/shared/opencode-config-dir.test.ts index 86d3afc55..5d6cf3ef5 100644 --- a/src/shared/opencode-config-dir.test.ts +++ b/src/shared/opencode-config-dir.test.ts @@ -1,6 +1,6 @@ import { describe, test, expect, beforeEach, afterEach } from "bun:test" import { homedir } from "node:os" -import { join, resolve } from "node:path" +import { join, resolve, win32 } from "node:path" import { getOpenCodeConfigDir, getOpenCodeConfigPaths, @@ -241,9 +241,10 @@ describe("opencode-config-dir", () => { // when getOpenCodeConfigDir is called with binary="opencode-desktop" const result = getOpenCodeConfigDir({ binary: "opencode-desktop", version: "1.0.200", checkExisting: false }) - // then returns %APPDATA%/ai.opencode.desktop - expect(result).toBe(join("C:\\Users\\TestUser\\AppData\\Roaming", TAURI_APP_IDENTIFIER)) + // then returns %APPDATA%/ai.opencode.desktop using Windows path semantics + expect(result).toBe(win32.join("C:\\Users\\TestUser\\AppData\\Roaming", TAURI_APP_IDENTIFIER)) }) + }) describe("dev build detection", () => { diff --git a/src/shared/opencode-config-dir.ts b/src/shared/opencode-config-dir.ts index cf4fc28da..e1dedc401 100644 --- a/src/shared/opencode-config-dir.ts +++ b/src/shared/opencode-config-dir.ts @@ -1,6 +1,6 @@ import { existsSync, realpathSync } from "node:fs" import { homedir } from "node:os" -import { join, resolve } from "node:path" +import { join, resolve, win32 } from "node:path" import type { OpenCodeBinaryType, @@ -31,7 +31,7 @@ function getTauriConfigDir(identifier: string): string { case "win32": { const appData = process.env.APPDATA || join(homedir(), "AppData", "Roaming") - return join(appData, identifier) + return win32.join(appData, identifier) } case "linux": @@ -71,7 +71,10 @@ export function getOpenCodeConfigDir(options: OpenCodeConfigDirOptions): string } const identifier = isDevBuild(version) ? TAURI_APP_IDENTIFIER_DEV : TAURI_APP_IDENTIFIER - const tauriDir = resolveConfigPath(getTauriConfigDir(identifier)) + const tauriDirBase = getTauriConfigDir(identifier) + const tauriDir = process.platform === "win32" + ? (win32.isAbsolute(tauriDirBase) ? win32.normalize(tauriDirBase) : win32.resolve(tauriDirBase)) + : resolveConfigPath(tauriDirBase) if (checkExisting) { const legacyDir = getCliConfigDir() diff --git a/test-setup.ts b/test-setup.ts index 5ac63e4e6..5c6e5aa0d 100644 --- a/test-setup.ts +++ b/test-setup.ts @@ -1,6 +1,8 @@ import { beforeEach } from "bun:test" -import { _resetForTesting } from "./src/features/claude-code-session-state/state" +import { _resetForTesting as resetClaudeSessionState } from "./src/features/claude-code-session-state/state" +import { _resetForTesting as resetModelFallbackState } from "./src/hooks/model-fallback/hook" beforeEach(() => { - _resetForTesting() + resetClaudeSessionState() + resetModelFallbackState() }) From da3e80464d2a93faeb679e4d914bb04bd53ec164 Mon Sep 17 00:00:00 2001 From: YeonGyu-Kim Date: Thu, 26 Mar 2026 11:22:00 +0900 Subject: [PATCH 26/63] fix(shared): add ancestor project discovery helpers Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus --- src/shared/index.ts | 1 + src/shared/project-discovery-dirs.test.ts | 74 +++++++++++++++++++++++ src/shared/project-discovery-dirs.ts | 52 ++++++++++++++++ 3 files changed, 127 insertions(+) create mode 100644 src/shared/project-discovery-dirs.test.ts create mode 100644 src/shared/project-discovery-dirs.ts diff --git a/src/shared/index.ts b/src/shared/index.ts index 726b55fa5..5d2615d70 100644 --- a/src/shared/index.ts +++ b/src/shared/index.ts @@ -67,5 +67,6 @@ export * from "./session-directory-resolver" export * from "./prompt-tools" export * from "./internal-initiator-marker" export * from "./plugin-command-discovery" +export * from "./project-discovery-dirs" export { SessionCategoryRegistry } from "./session-category-registry" export * from "./plugin-identity" diff --git a/src/shared/project-discovery-dirs.test.ts b/src/shared/project-discovery-dirs.test.ts new file mode 100644 index 000000000..13dcc8a71 --- /dev/null +++ b/src/shared/project-discovery-dirs.test.ts @@ -0,0 +1,74 @@ +import { afterEach, beforeEach, describe, expect, it } from "bun:test" +import { mkdirSync, rmSync } from "node:fs" +import { tmpdir } from "node:os" +import { join } from "node:path" +import { + findProjectAgentsSkillDirs, + findProjectClaudeSkillDirs, + findProjectOpencodeCommandDirs, + findProjectOpencodeSkillDirs, +} from "./project-discovery-dirs" + +const TEST_DIR = join(tmpdir(), `project-discovery-dirs-${Date.now()}`) + +describe("project-discovery-dirs", () => { + beforeEach(() => { + mkdirSync(TEST_DIR, { recursive: true }) + }) + + afterEach(() => { + rmSync(TEST_DIR, { recursive: true, force: true }) + }) + + it("#given nested .opencode skill directories #when finding project opencode skill dirs #then returns nearest-first with aliases", () => { + // given + const projectDir = join(TEST_DIR, "project") + const childDir = join(projectDir, "apps", "cli") + mkdirSync(join(projectDir, ".opencode", "skill"), { recursive: true }) + mkdirSync(join(projectDir, ".opencode", "skills"), { recursive: true }) + mkdirSync(join(TEST_DIR, ".opencode", "skills"), { recursive: true }) + + // when + const directories = findProjectOpencodeSkillDirs(childDir) + + // then + expect(directories).toEqual([ + join(projectDir, ".opencode", "skills"), + join(projectDir, ".opencode", "skill"), + join(TEST_DIR, ".opencode", "skills"), + ]) + }) + + it("#given nested .opencode command directories #when finding project opencode command dirs #then returns nearest-first with aliases", () => { + // given + const projectDir = join(TEST_DIR, "project") + const childDir = join(projectDir, "packages", "tool") + mkdirSync(join(projectDir, ".opencode", "commands"), { recursive: true }) + mkdirSync(join(TEST_DIR, ".opencode", "command"), { recursive: true }) + + // when + const directories = findProjectOpencodeCommandDirs(childDir) + + // then + expect(directories).toEqual([ + join(projectDir, ".opencode", "commands"), + join(TEST_DIR, ".opencode", "command"), + ]) + }) + + it("#given ancestor claude and agents skill directories #when finding project compatibility dirs #then discovers both scopes", () => { + // given + const projectDir = join(TEST_DIR, "project") + const childDir = join(projectDir, "src", "nested") + mkdirSync(join(projectDir, ".claude", "skills"), { recursive: true }) + mkdirSync(join(TEST_DIR, ".agents", "skills"), { recursive: true }) + + // when + const claudeDirectories = findProjectClaudeSkillDirs(childDir) + const agentsDirectories = findProjectAgentsSkillDirs(childDir) + + // then + expect(claudeDirectories).toEqual([join(projectDir, ".claude", "skills")]) + expect(agentsDirectories).toEqual([join(TEST_DIR, ".agents", "skills")]) + }) +}) diff --git a/src/shared/project-discovery-dirs.ts b/src/shared/project-discovery-dirs.ts new file mode 100644 index 000000000..007c3c16b --- /dev/null +++ b/src/shared/project-discovery-dirs.ts @@ -0,0 +1,52 @@ +import { existsSync } from "node:fs" +import { dirname, join, resolve } from "node:path" + +function findAncestorDirectories( + startDirectory: string, + targetPaths: ReadonlyArray>, +): string[] { + const directories: string[] = [] + const seen = new Set() + let currentDirectory = resolve(startDirectory) + + while (true) { + for (const targetPath of targetPaths) { + const candidateDirectory = join(currentDirectory, ...targetPath) + if (!existsSync(candidateDirectory) || seen.has(candidateDirectory)) { + continue + } + + seen.add(candidateDirectory) + directories.push(candidateDirectory) + } + + const parentDirectory = dirname(currentDirectory) + if (parentDirectory === currentDirectory) { + return directories + } + + currentDirectory = parentDirectory + } +} + +export function findProjectClaudeSkillDirs(startDirectory: string): string[] { + return findAncestorDirectories(startDirectory, [[".claude", "skills"]]) +} + +export function findProjectAgentsSkillDirs(startDirectory: string): string[] { + return findAncestorDirectories(startDirectory, [[".agents", "skills"]]) +} + +export function findProjectOpencodeSkillDirs(startDirectory: string): string[] { + return findAncestorDirectories(startDirectory, [ + [".opencode", "skills"], + [".opencode", "skill"], + ]) +} + +export function findProjectOpencodeCommandDirs(startDirectory: string): string[] { + return findAncestorDirectories(startDirectory, [ + [".opencode", "commands"], + [".opencode", "command"], + ]) +} From 6d688ac0ae13686913c74ee2283be875deeed030 Mon Sep 17 00:00:00 2001 From: YeonGyu-Kim Date: Thu, 26 Mar 2026 11:22:00 +0900 Subject: [PATCH 27/63] fix(shared): support opencode directory aliases Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus --- src/shared/opencode-command-dirs.test.ts | 11 ++++++++--- src/shared/opencode-command-dirs.ts | 6 ++++-- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/src/shared/opencode-command-dirs.test.ts b/src/shared/opencode-command-dirs.test.ts index 02ffb871f..4b2ac48f0 100644 --- a/src/shared/opencode-command-dirs.test.ts +++ b/src/shared/opencode-command-dirs.test.ts @@ -26,8 +26,10 @@ describe("opencode-command-dirs", () => { const dirs = getOpenCodeSkillDirs({ binary: "opencode" }) expect(dirs).toContain("/home/user/.config/opencode/profiles/opus/skills") + expect(dirs).toContain("/home/user/.config/opencode/profiles/opus/skill") expect(dirs).toContain("/home/user/.config/opencode/skills") - expect(dirs).toHaveLength(2) + expect(dirs).toContain("/home/user/.config/opencode/skill") + expect(dirs).toHaveLength(4) }) }) }) @@ -41,7 +43,8 @@ describe("opencode-command-dirs", () => { const dirs = getOpenCodeSkillDirs({ binary: "opencode" }) expect(dirs).toContain("/home/user/.config/opencode/skills") - expect(dirs).toHaveLength(1) + expect(dirs).toContain("/home/user/.config/opencode/skill") + expect(dirs).toHaveLength(2) }) }) }) @@ -56,9 +59,11 @@ describe("opencode-command-dirs", () => { const { getOpenCodeCommandDirs } = await import("./opencode-command-dirs") const dirs = getOpenCodeCommandDirs({ binary: "opencode" }) + expect(dirs).toContain("/home/user/.config/opencode/profiles/opus/commands") expect(dirs).toContain("/home/user/.config/opencode/profiles/opus/command") + expect(dirs).toContain("/home/user/.config/opencode/commands") expect(dirs).toContain("/home/user/.config/opencode/command") - expect(dirs).toHaveLength(2) + expect(dirs).toHaveLength(4) }) }) }) diff --git a/src/shared/opencode-command-dirs.ts b/src/shared/opencode-command-dirs.ts index 456085730..4431370ad 100644 --- a/src/shared/opencode-command-dirs.ts +++ b/src/shared/opencode-command-dirs.ts @@ -17,8 +17,9 @@ export function getOpenCodeCommandDirs(options: OpenCodeConfigDirOptions): strin return Array.from( new Set([ + join(configDir, "commands"), join(configDir, "command"), - ...(parentConfigDir ? [join(parentConfigDir, "command")] : []), + ...(parentConfigDir ? [join(parentConfigDir, "commands"), join(parentConfigDir, "command")] : []), ]) ) } @@ -30,7 +31,8 @@ export function getOpenCodeSkillDirs(options: OpenCodeConfigDirOptions): string[ return Array.from( new Set([ join(configDir, "skills"), - ...(parentConfigDir ? [join(parentConfigDir, "skills")] : []), + join(configDir, "skill"), + ...(parentConfigDir ? [join(parentConfigDir, "skills"), join(parentConfigDir, "skill")] : []), ]) ) } From 82425008561fedb32485217ea9559794737eb7ba Mon Sep 17 00:00:00 2001 From: YeonGyu-Kim Date: Thu, 26 Mar 2026 11:22:00 +0900 Subject: [PATCH 28/63] fix(skills): expand tilde config source paths Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus --- .../config-source-discovery.test.ts | 24 ++++++++++++++++++- .../config-source-discovery.ts | 9 +++++++ 2 files changed, 32 insertions(+), 1 deletion(-) diff --git a/src/features/opencode-skill-loader/config-source-discovery.test.ts b/src/features/opencode-skill-loader/config-source-discovery.test.ts index d10303ce0..091118ce6 100644 --- a/src/features/opencode-skill-loader/config-source-discovery.test.ts +++ b/src/features/opencode-skill-loader/config-source-discovery.test.ts @@ -1,7 +1,7 @@ import { afterEach, beforeEach, describe, expect, it } from "bun:test" import { mkdirSync, rmSync, writeFileSync } from "fs" import { join } from "path" -import { tmpdir } from "os" +import { homedir, tmpdir } from "os" import { SkillsConfigSchema } from "../../config/schema/skills" import { discoverConfigSourceSkills, normalizePathForGlob } from "./config-source-discovery" @@ -69,6 +69,28 @@ describe("config source discovery", () => { expect(names).not.toContain("skip/skipped-skill") }) + it("loads skills from ~/ sources path", async () => { + // given + const homeSkillsDir = join(homedir(), `.omo-config-source-${Date.now()}`) + writeSkill(join(homeSkillsDir, "tilde-skill"), "tilde-skill", "Loaded from tilde path") + const config = SkillsConfigSchema.parse({ + sources: [{ path: `~/${homeSkillsDir.split(homedir())[1]?.replace(/^\//, "")}`, recursive: true }], + }) + + try { + // when + const skills = await discoverConfigSourceSkills({ + config, + configDir: join(TEST_DIR, "config"), + }) + + // then + expect(skills.some((skill) => skill.name === "tilde-skill")).toBe(true) + } finally { + rmSync(homeSkillsDir, { recursive: true, force: true }) + } + }) + it("normalizes windows separators before glob matching", () => { // given const windowsPath = "keep\\nested\\SKILL.md" diff --git a/src/features/opencode-skill-loader/config-source-discovery.ts b/src/features/opencode-skill-loader/config-source-discovery.ts index df3ee653e..b290c8b30 100644 --- a/src/features/opencode-skill-loader/config-source-discovery.ts +++ b/src/features/opencode-skill-loader/config-source-discovery.ts @@ -1,4 +1,5 @@ import { promises as fs } from "fs" +import { homedir } from "os" import { dirname, extname, isAbsolute, join, relative } from "path" import picomatch from "picomatch" import type { SkillsConfig } from "../../config/schema" @@ -15,6 +16,14 @@ function isHttpUrl(path: string): boolean { } function toAbsolutePath(path: string, configDir: string): string { + if (path === "~") { + return homedir() + } + + if (path.startsWith("~/")) { + return join(homedir(), path.slice(2)) + } + if (isAbsolute(path)) { return path } From b5cb50b561ddf08da68dddba6157c11c936f9423 Mon Sep 17 00:00:00 2001 From: YeonGyu-Kim Date: Thu, 26 Mar 2026 11:22:00 +0900 Subject: [PATCH 29/63] fix(skills): discover ancestor project skill directories Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus --- .../opencode-skill-loader/loader.test.ts | 87 +++++++++++++++++++ src/features/opencode-skill-loader/loader.ts | 50 ++++++++--- 2 files changed, 125 insertions(+), 12 deletions(-) diff --git a/src/features/opencode-skill-loader/loader.test.ts b/src/features/opencode-skill-loader/loader.test.ts index 7aecb801f..c5042e0b1 100644 --- a/src/features/opencode-skill-loader/loader.test.ts +++ b/src/features/opencode-skill-loader/loader.test.ts @@ -615,5 +615,92 @@ Skill body. expect(skill).toBeDefined() expect(skill?.scope).toBe("project") }) + + it("#given a skill in ancestor .agents/skills/ #when discoverProjectAgentsSkills is called from child directory #then it discovers the ancestor skill", async () => { + // given + const skillContent = `--- +name: ancestor-agent-skill +description: A skill from ancestor .agents/skills directory +--- +Skill body. +` + const projectDir = join(TEST_DIR, "project") + const childDir = join(projectDir, "apps", "worker") + const agentsProjectSkillsDir = join(projectDir, ".agents", "skills") + const skillDir = join(agentsProjectSkillsDir, "ancestor-agent-skill") + mkdirSync(childDir, { recursive: true }) + mkdirSync(skillDir, { recursive: true }) + writeFileSync(join(skillDir, "SKILL.md"), skillContent) + + // when + const { discoverProjectAgentsSkills } = await import("./loader") + const skills = await discoverProjectAgentsSkills(childDir) + const skill = skills.find((candidate) => candidate.name === "ancestor-agent-skill") + + // then + expect(skill).toBeDefined() + expect(skill?.scope).toBe("project") + }) + }) + + describe("opencode project skill discovery", () => { + it("#given a skill in ancestor .opencode/skills/ #when discoverOpencodeProjectSkills is called from child directory #then it discovers the ancestor skill", async () => { + // given + const skillContent = `--- +name: ancestor-opencode-skill +description: A skill from ancestor .opencode/skills directory +--- +Skill body. +` + const projectDir = join(TEST_DIR, "project") + const childDir = join(projectDir, "packages", "cli") + const skillsDir = join(projectDir, ".opencode", "skills", "ancestor-opencode-skill") + mkdirSync(childDir, { recursive: true }) + mkdirSync(skillsDir, { recursive: true }) + writeFileSync(join(skillsDir, "SKILL.md"), skillContent) + + // when + const { discoverOpencodeProjectSkills } = await import("./loader") + const skills = await discoverOpencodeProjectSkills(childDir) + const skill = skills.find((candidate) => candidate.name === "ancestor-opencode-skill") + + // then + expect(skill).toBeDefined() + expect(skill?.scope).toBe("opencode-project") + }) + + it("#given a skill in .opencode/skill/ #when discoverOpencodeProjectSkills is called #then it discovers the singular alias directory", async () => { + // given + const skillContent = `--- +name: singular-opencode-skill +description: A skill from .opencode/skill directory +--- +Skill body. +` + const singularSkillDir = join( + TEST_DIR, + ".opencode", + "skill", + "singular-opencode-skill", + ) + mkdirSync(singularSkillDir, { recursive: true }) + writeFileSync(join(singularSkillDir, "SKILL.md"), skillContent) + + // when + const { discoverOpencodeProjectSkills } = await import("./loader") + const originalCwd = process.cwd() + process.chdir(TEST_DIR) + + try { + const skills = await discoverOpencodeProjectSkills() + const skill = skills.find((candidate) => candidate.name === "singular-opencode-skill") + + // then + expect(skill).toBeDefined() + expect(skill?.scope).toBe("opencode-project") + } finally { + process.chdir(originalCwd) + } + }) }) }) diff --git a/src/features/opencode-skill-loader/loader.ts b/src/features/opencode-skill-loader/loader.ts index 205267e3e..e577809fe 100644 --- a/src/features/opencode-skill-loader/loader.ts +++ b/src/features/opencode-skill-loader/loader.ts @@ -3,6 +3,11 @@ import { homedir } from "os" import { getClaudeConfigDir } from "../../shared/claude-config-dir" import { getOpenCodeConfigDir } from "../../shared/opencode-config-dir" import { getOpenCodeSkillDirs } from "../../shared/opencode-command-dirs" +import { + findProjectAgentsSkillDirs, + findProjectClaudeSkillDirs, + findProjectOpencodeSkillDirs, +} from "../../shared/project-discovery-dirs" import type { CommandDefinition } from "../claude-code-command-loader/types" import type { LoadedSkill } from "./types" import { skillsToCommandDefinitionRecord } from "./skill-definition-record" @@ -16,9 +21,11 @@ export async function loadUserSkills(): Promise> { - const projectSkillsDir = join(directory ?? process.cwd(), ".claude", "skills") - const skills = await loadSkillsFromDir({ skillsDir: projectSkillsDir, scope: "project" }) - return skillsToCommandDefinitionRecord(skills) + const projectSkillDirs = findProjectClaudeSkillDirs(directory ?? process.cwd()) + const allSkills = await Promise.all( + projectSkillDirs.map((skillsDir) => loadSkillsFromDir({ skillsDir, scope: "project" })), + ) + return skillsToCommandDefinitionRecord(deduplicateSkillsByName(allSkills.flat())) } export async function loadOpencodeGlobalSkills(): Promise> { @@ -30,9 +37,15 @@ export async function loadOpencodeGlobalSkills(): Promise> { - const opencodeProjectDir = join(directory ?? process.cwd(), ".opencode", "skills") - const skills = await loadSkillsFromDir({ skillsDir: opencodeProjectDir, scope: "opencode-project" }) - return skillsToCommandDefinitionRecord(skills) + const opencodeProjectSkillDirs = findProjectOpencodeSkillDirs( + directory ?? process.cwd(), + ) + const allSkills = await Promise.all( + opencodeProjectSkillDirs.map((skillsDir) => + loadSkillsFromDir({ skillsDir, scope: "opencode-project" }), + ), + ) + return skillsToCommandDefinitionRecord(deduplicateSkillsByName(allSkills.flat())) } export interface DiscoverSkillsOptions { @@ -104,8 +117,11 @@ export async function discoverUserClaudeSkills(): Promise { } export async function discoverProjectClaudeSkills(directory?: string): Promise { - const projectSkillsDir = join(directory ?? process.cwd(), ".claude", "skills") - return loadSkillsFromDir({ skillsDir: projectSkillsDir, scope: "project" }) + const projectSkillDirs = findProjectClaudeSkillDirs(directory ?? process.cwd()) + const allSkills = await Promise.all( + projectSkillDirs.map((skillsDir) => loadSkillsFromDir({ skillsDir, scope: "project" })), + ) + return deduplicateSkillsByName(allSkills.flat()) } export async function discoverOpencodeGlobalSkills(): Promise { @@ -117,13 +133,23 @@ export async function discoverOpencodeGlobalSkills(): Promise { } export async function discoverOpencodeProjectSkills(directory?: string): Promise { - const opencodeProjectDir = join(directory ?? process.cwd(), ".opencode", "skills") - return loadSkillsFromDir({ skillsDir: opencodeProjectDir, scope: "opencode-project" }) + const opencodeProjectSkillDirs = findProjectOpencodeSkillDirs( + directory ?? process.cwd(), + ) + const allSkills = await Promise.all( + opencodeProjectSkillDirs.map((skillsDir) => + loadSkillsFromDir({ skillsDir, scope: "opencode-project" }), + ), + ) + return deduplicateSkillsByName(allSkills.flat()) } export async function discoverProjectAgentsSkills(directory?: string): Promise { - const agentsProjectDir = join(directory ?? process.cwd(), ".agents", "skills") - return loadSkillsFromDir({ skillsDir: agentsProjectDir, scope: "project" }) + const agentsProjectSkillDirs = findProjectAgentsSkillDirs(directory ?? process.cwd()) + const allSkills = await Promise.all( + agentsProjectSkillDirs.map((skillsDir) => loadSkillsFromDir({ skillsDir, scope: "project" })), + ) + return deduplicateSkillsByName(allSkills.flat()) } export async function discoverGlobalAgentsSkills(): Promise { From 28bcab066e9d22bf4371052ea2d36bda4d2d4376 Mon Sep 17 00:00:00 2001 From: YeonGyu-Kim Date: Thu, 26 Mar 2026 11:22:00 +0900 Subject: [PATCH 30/63] fix(commands): load opencode command dirs from aliases Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus --- .../claude-code-command-loader/loader.test.ts | 70 +++++++++++++++++++ .../claude-code-command-loader/loader.ts | 26 ++++--- 2 files changed, 88 insertions(+), 8 deletions(-) create mode 100644 src/features/claude-code-command-loader/loader.test.ts diff --git a/src/features/claude-code-command-loader/loader.test.ts b/src/features/claude-code-command-loader/loader.test.ts new file mode 100644 index 000000000..490a31f4a --- /dev/null +++ b/src/features/claude-code-command-loader/loader.test.ts @@ -0,0 +1,70 @@ +import { afterEach, beforeEach, describe, expect, it } from "bun:test" +import { mkdirSync, rmSync, writeFileSync } from "node:fs" +import { tmpdir } from "node:os" +import { join } from "node:path" +import { loadOpencodeGlobalCommands, loadOpencodeProjectCommands } from "./loader" + +const TEST_DIR = join(tmpdir(), `claude-code-command-loader-${Date.now()}`) + +function writeCommand(directory: string, name: string, description: string): void { + mkdirSync(directory, { recursive: true }) + writeFileSync( + join(directory, `${name}.md`), + `---\ndescription: ${description}\n---\nRun ${name}.\n`, + ) +} + +describe("claude-code command loader", () => { + let originalOpencodeConfigDir: string | undefined + + beforeEach(() => { + mkdirSync(TEST_DIR, { recursive: true }) + originalOpencodeConfigDir = process.env.OPENCODE_CONFIG_DIR + }) + + afterEach(() => { + if (originalOpencodeConfigDir === undefined) { + delete process.env.OPENCODE_CONFIG_DIR + } else { + process.env.OPENCODE_CONFIG_DIR = originalOpencodeConfigDir + } + rmSync(TEST_DIR, { recursive: true, force: true }) + }) + + it("#given a parent .opencode/commands directory #when loadOpencodeProjectCommands is called from child directory #then it loads the ancestor command", async () => { + // given + const projectDir = join(TEST_DIR, "project") + const childDir = join(projectDir, "apps", "desktop") + writeCommand(join(projectDir, ".opencode", "commands"), "ancestor", "Ancestor command") + + // when + const commands = await loadOpencodeProjectCommands(childDir) + + // then + expect(commands.ancestor?.description).toBe("(opencode-project) Ancestor command") + }) + + it("#given a .opencode/command directory #when loadOpencodeProjectCommands is called #then it loads the singular alias directory", async () => { + // given + writeCommand(join(TEST_DIR, ".opencode", "command"), "singular", "Singular command") + + // when + const commands = await loadOpencodeProjectCommands(TEST_DIR) + + // then + expect(commands.singular?.description).toBe("(opencode-project) Singular command") + }) + + it("#given a global .opencode/commands directory #when loadOpencodeGlobalCommands is called #then it loads the plural alias directory", async () => { + // given + const opencodeConfigDir = join(TEST_DIR, "opencode-config") + process.env.OPENCODE_CONFIG_DIR = opencodeConfigDir + writeCommand(join(opencodeConfigDir, "commands"), "global-plural", "Global plural command") + + // when + const commands = await loadOpencodeGlobalCommands() + + // then + expect(commands["global-plural"]?.description).toBe("(opencode) Global plural command") + }) +}) diff --git a/src/features/claude-code-command-loader/loader.ts b/src/features/claude-code-command-loader/loader.ts index adf2cc4c1..152bf21f9 100644 --- a/src/features/claude-code-command-loader/loader.ts +++ b/src/features/claude-code-command-loader/loader.ts @@ -3,7 +3,12 @@ import { join, basename } from "path" import { parseFrontmatter } from "../../shared/frontmatter" import { sanitizeModelField } from "../../shared/model-sanitizer" import { isMarkdownFile } from "../../shared/file-utils" -import { getClaudeConfigDir, getOpenCodeConfigDir } from "../../shared" +import { + findProjectOpencodeCommandDirs, + getClaudeConfigDir, + getOpenCodeCommandDirs, + getOpenCodeConfigDir, +} from "../../shared" import { log } from "../../shared/logger" import type { CommandScope, CommandDefinition, CommandFrontmatter, LoadedCommand } from "./types" @@ -121,16 +126,21 @@ export async function loadProjectCommands(directory?: string): Promise> { - const configDir = getOpenCodeConfigDir({ binary: "opencode" }) - const opencodeCommandsDir = join(configDir, "command") - const commands = await loadCommandsFromDir(opencodeCommandsDir, "opencode") - return commandsToRecord(commands) + const opencodeCommandDirs = getOpenCodeCommandDirs({ binary: "opencode" }) + const allCommands = await Promise.all( + opencodeCommandDirs.map((commandsDir) => loadCommandsFromDir(commandsDir, "opencode")), + ) + return commandsToRecord(allCommands.flat()) } export async function loadOpencodeProjectCommands(directory?: string): Promise> { - const opencodeProjectDir = join(directory ?? process.cwd(), ".opencode", "command") - const commands = await loadCommandsFromDir(opencodeProjectDir, "opencode-project") - return commandsToRecord(commands) + const opencodeProjectDirs = findProjectOpencodeCommandDirs(directory ?? process.cwd()) + const allCommands = await Promise.all( + opencodeProjectDirs.map((commandsDir) => + loadCommandsFromDir(commandsDir, "opencode-project"), + ), + ) + return commandsToRecord(allCommands.flat()) } export async function loadAllCommands(directory?: string): Promise> { From b6ee7f09b1a35a219dd8b7782fda49e1896b5d0b Mon Sep 17 00:00:00 2001 From: YeonGyu-Kim Date: Thu, 26 Mar 2026 11:22:00 +0900 Subject: [PATCH 31/63] fix(slashcommand): discover ancestor opencode commands Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus --- .../slashcommand/command-discovery.test.ts | 23 +++++++++++++++++++ src/tools/slashcommand/command-discovery.ts | 7 ++++-- 2 files changed, 28 insertions(+), 2 deletions(-) diff --git a/src/tools/slashcommand/command-discovery.test.ts b/src/tools/slashcommand/command-discovery.test.ts index 36e59ba0c..1ebf65c97 100644 --- a/src/tools/slashcommand/command-discovery.test.ts +++ b/src/tools/slashcommand/command-discovery.test.ts @@ -181,4 +181,27 @@ Use parent opencode commit command. expect(commitCommand?.scope).toBe("opencode") expect(commitCommand?.content).toContain("Use parent opencode commit command.") }) + + it("discovers ancestor project opencode commands from plural commands directory", () => { + const projectRoot = join(projectDir, "workspace") + const childDir = join(projectRoot, "apps", "cli") + const commandsDir = join(projectRoot, ".opencode", "commands") + + mkdirSync(childDir, { recursive: true }) + mkdirSync(commandsDir, { recursive: true }) + writeFileSync( + join(commandsDir, "ancestor.md"), + `--- +description: Discover command from ancestor plural directory +--- +Use ancestor command. +`, + ) + + const commands = discoverCommandsSync(childDir) + const ancestorCommand = commands.find((command) => command.name === "ancestor") + + expect(ancestorCommand?.scope).toBe("opencode-project") + expect(ancestorCommand?.content).toContain("Use ancestor command.") + }) }) diff --git a/src/tools/slashcommand/command-discovery.ts b/src/tools/slashcommand/command-discovery.ts index 7fb96c322..ad8e23130 100644 --- a/src/tools/slashcommand/command-discovery.ts +++ b/src/tools/slashcommand/command-discovery.ts @@ -3,6 +3,7 @@ import { basename, join } from "path" import { parseFrontmatter, sanitizeModelField, + findProjectOpencodeCommandDirs, getOpenCodeCommandDirs, discoverPluginCommandDefinitions, } from "../../shared" @@ -82,14 +83,16 @@ export function discoverCommandsSync( const userCommandsDir = join(getClaudeConfigDir(), "commands") const projectCommandsDir = join(directory ?? process.cwd(), ".claude", "commands") const opencodeGlobalDirs = getOpenCodeCommandDirs({ binary: "opencode" }) - const opencodeProjectDir = join(directory ?? process.cwd(), ".opencode", "command") + const opencodeProjectDirs = findProjectOpencodeCommandDirs(directory ?? process.cwd()) const userCommands = discoverCommandsFromDir(userCommandsDir, "user") const opencodeGlobalCommands = opencodeGlobalDirs.flatMap((commandsDir) => discoverCommandsFromDir(commandsDir, "opencode") ) const projectCommands = discoverCommandsFromDir(projectCommandsDir, "project") - const opencodeProjectCommands = discoverCommandsFromDir(opencodeProjectDir, "opencode-project") + const opencodeProjectCommands = opencodeProjectDirs.flatMap((commandsDir) => + discoverCommandsFromDir(commandsDir, "opencode-project"), + ) const pluginCommands = discoverPluginCommands(options) const builtinCommandsMap = loadBuiltinCommands() From 9fde3708389ff4e5fbd5fa7d99313b9d32107385 Mon Sep 17 00:00:00 2001 From: YeonGyu-Kim Date: Thu, 26 Mar 2026 11:36:59 +0900 Subject: [PATCH 32/63] fix(commands): preserve nearest opencode command precedence Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus --- .../claude-code-command-loader/loader.test.ts | 31 +++++++++++++++++++ .../claude-code-command-loader/loader.ts | 18 ++++++++++- 2 files changed, 48 insertions(+), 1 deletion(-) diff --git a/src/features/claude-code-command-loader/loader.test.ts b/src/features/claude-code-command-loader/loader.test.ts index 490a31f4a..dde2096c0 100644 --- a/src/features/claude-code-command-loader/loader.test.ts +++ b/src/features/claude-code-command-loader/loader.test.ts @@ -55,6 +55,22 @@ describe("claude-code command loader", () => { expect(commands.singular?.description).toBe("(opencode-project) Singular command") }) + it("#given duplicate project command names across ancestors #when loadOpencodeProjectCommands is called #then the nearest directory wins", async () => { + // given + const projectRoot = join(TEST_DIR, "project") + const childDir = join(projectRoot, "apps", "desktop") + const ancestorDir = join(TEST_DIR, ".opencode", "commands") + const projectDir = join(projectRoot, ".opencode", "commands") + writeCommand(ancestorDir, "duplicate", "Ancestor command") + writeCommand(projectDir, "duplicate", "Nearest command") + + // when + const commands = await loadOpencodeProjectCommands(childDir) + + // then + expect(commands.duplicate?.description).toBe("(opencode-project) Nearest command") + }) + it("#given a global .opencode/commands directory #when loadOpencodeGlobalCommands is called #then it loads the plural alias directory", async () => { // given const opencodeConfigDir = join(TEST_DIR, "opencode-config") @@ -67,4 +83,19 @@ describe("claude-code command loader", () => { // then expect(commands["global-plural"]?.description).toBe("(opencode) Global plural command") }) + + it("#given duplicate global command names across profile and parent dirs #when loadOpencodeGlobalCommands is called #then the profile dir wins", async () => { + // given + const opencodeRootDir = join(TEST_DIR, "opencode-root") + const profileConfigDir = join(opencodeRootDir, "profiles", "codex") + process.env.OPENCODE_CONFIG_DIR = profileConfigDir + writeCommand(join(opencodeRootDir, "commands"), "duplicate-global", "Parent global command") + writeCommand(join(profileConfigDir, "commands"), "duplicate-global", "Profile global command") + + // when + const commands = await loadOpencodeGlobalCommands() + + // then + expect(commands["duplicate-global"]?.description).toBe("(opencode) Profile global command") + }) }) diff --git a/src/features/claude-code-command-loader/loader.ts b/src/features/claude-code-command-loader/loader.ts index 152bf21f9..aee1f6e59 100644 --- a/src/features/claude-code-command-loader/loader.ts +++ b/src/features/claude-code-command-loader/loader.ts @@ -104,9 +104,25 @@ $ARGUMENTS return commands } +function deduplicateLoadedCommandsByName(commands: LoadedCommand[]): LoadedCommand[] { + const seen = new Set() + const deduplicatedCommands: LoadedCommand[] = [] + + for (const command of commands) { + if (seen.has(command.name)) { + continue + } + + seen.add(command.name) + deduplicatedCommands.push(command) + } + + return deduplicatedCommands +} + function commandsToRecord(commands: LoadedCommand[]): Record { const result: Record = {} - for (const cmd of commands) { + for (const cmd of deduplicateLoadedCommandsByName(commands)) { const { name: _name, argumentHint: _argumentHint, ...openCodeCompatible } = cmd.definition result[cmd.name] = openCodeCompatible as CommandDefinition } From 94b4a4f850ef5ae9ff7a069b44d2f01989218924 Mon Sep 17 00:00:00 2001 From: YeonGyu-Kim Date: Thu, 26 Mar 2026 11:36:59 +0900 Subject: [PATCH 33/63] fix(slashcommand): deduplicate opencode command aliases Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus --- .../slashcommand/command-discovery.test.ts | 31 +++++++++++++++++++ src/tools/slashcommand/command-discovery.ts | 20 ++++++++++-- 2 files changed, 49 insertions(+), 2 deletions(-) diff --git a/src/tools/slashcommand/command-discovery.test.ts b/src/tools/slashcommand/command-discovery.test.ts index 1ebf65c97..232515a33 100644 --- a/src/tools/slashcommand/command-discovery.test.ts +++ b/src/tools/slashcommand/command-discovery.test.ts @@ -204,4 +204,35 @@ Use ancestor command. expect(ancestorCommand?.scope).toBe("opencode-project") expect(ancestorCommand?.content).toContain("Use ancestor command.") }) + + it("deduplicates same-named opencode commands while keeping the higher-priority alias", () => { + const commandsRoot = join(projectDir, ".opencode") + const singularDir = join(commandsRoot, "command") + const pluralDir = join(commandsRoot, "commands") + + mkdirSync(singularDir, { recursive: true }) + mkdirSync(pluralDir, { recursive: true }) + writeFileSync( + join(singularDir, "duplicate.md"), + `--- +description: Singular duplicate command +--- +Use singular command. +`, + ) + writeFileSync( + join(pluralDir, "duplicate.md"), + `--- +description: Plural duplicate command +--- +Use plural command. +`, + ) + + const commands = discoverCommandsSync(projectDir) + const duplicates = commands.filter((command) => command.name === "duplicate") + + expect(duplicates).toHaveLength(1) + expect(duplicates[0]?.content).toContain("Use plural command.") + }) }) diff --git a/src/tools/slashcommand/command-discovery.ts b/src/tools/slashcommand/command-discovery.ts index ad8e23130..7567e74dd 100644 --- a/src/tools/slashcommand/command-discovery.ts +++ b/src/tools/slashcommand/command-discovery.ts @@ -76,6 +76,22 @@ function discoverPluginCommands(options?: CommandDiscoveryOptions): CommandInfo[ })) } +function deduplicateCommandInfosByName(commands: CommandInfo[]): CommandInfo[] { + const seen = new Set() + const deduplicatedCommands: CommandInfo[] = [] + + for (const command of commands) { + if (seen.has(command.name)) { + continue + } + + seen.add(command.name) + deduplicatedCommands.push(command) + } + + return deduplicatedCommands +} + export function discoverCommandsSync( directory?: string, options?: CommandDiscoveryOptions, @@ -110,12 +126,12 @@ export function discoverCommandsSync( scope: "builtin", })) - return [ + return deduplicateCommandInfosByName([ ...projectCommands, ...userCommands, ...opencodeProjectCommands, ...opencodeGlobalCommands, ...builtinCommands, ...pluginCommands, - ] + ]) } From 83819a15d3035eb3345165b6594b32359eb919ca Mon Sep 17 00:00:00 2001 From: YeonGyu-Kim Date: Thu, 26 Mar 2026 12:15:47 +0900 Subject: [PATCH 34/63] fix(shared): stop ancestor discovery at worktree root Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus --- src/shared/project-discovery-dirs.test.ts | 34 +++++++--- src/shared/project-discovery-dirs.ts | 83 ++++++++++++++++++----- 2 files changed, 92 insertions(+), 25 deletions(-) diff --git a/src/shared/project-discovery-dirs.test.ts b/src/shared/project-discovery-dirs.test.ts index 13dcc8a71..39ba5dc13 100644 --- a/src/shared/project-discovery-dirs.test.ts +++ b/src/shared/project-discovery-dirs.test.ts @@ -1,5 +1,5 @@ import { afterEach, beforeEach, describe, expect, it } from "bun:test" -import { mkdirSync, rmSync } from "node:fs" +import { mkdirSync, realpathSync, rmSync } from "node:fs" import { tmpdir } from "node:os" import { join } from "node:path" import { @@ -11,6 +11,10 @@ import { const TEST_DIR = join(tmpdir(), `project-discovery-dirs-${Date.now()}`) +function canonicalPath(path: string): string { + return realpathSync(path) +} + describe("project-discovery-dirs", () => { beforeEach(() => { mkdirSync(TEST_DIR, { recursive: true }) @@ -33,9 +37,9 @@ describe("project-discovery-dirs", () => { // then expect(directories).toEqual([ - join(projectDir, ".opencode", "skills"), - join(projectDir, ".opencode", "skill"), - join(TEST_DIR, ".opencode", "skills"), + canonicalPath(join(projectDir, ".opencode", "skills")), + canonicalPath(join(projectDir, ".opencode", "skill")), + canonicalPath(join(TEST_DIR, ".opencode", "skills")), ]) }) @@ -51,8 +55,8 @@ describe("project-discovery-dirs", () => { // then expect(directories).toEqual([ - join(projectDir, ".opencode", "commands"), - join(TEST_DIR, ".opencode", "command"), + canonicalPath(join(projectDir, ".opencode", "commands")), + canonicalPath(join(TEST_DIR, ".opencode", "command")), ]) }) @@ -68,7 +72,21 @@ describe("project-discovery-dirs", () => { const agentsDirectories = findProjectAgentsSkillDirs(childDir) // then - expect(claudeDirectories).toEqual([join(projectDir, ".claude", "skills")]) - expect(agentsDirectories).toEqual([join(TEST_DIR, ".agents", "skills")]) + expect(claudeDirectories).toEqual([canonicalPath(join(projectDir, ".claude", "skills"))]) + expect(agentsDirectories).toEqual([canonicalPath(join(TEST_DIR, ".agents", "skills"))]) + }) + + it("#given a stop directory #when finding ancestor dirs #then it does not scan beyond the stop boundary", () => { + // given + const projectDir = join(TEST_DIR, "project") + const childDir = join(projectDir, "apps", "cli") + mkdirSync(join(projectDir, ".opencode", "skills"), { recursive: true }) + mkdirSync(join(TEST_DIR, ".opencode", "skills"), { recursive: true }) + + // when + const directories = findProjectOpencodeSkillDirs(childDir, projectDir) + + // then + expect(directories).toEqual([canonicalPath(join(projectDir, ".opencode", "skills"))]) }) }) diff --git a/src/shared/project-discovery-dirs.ts b/src/shared/project-discovery-dirs.ts index 007c3c16b..4e22b66f6 100644 --- a/src/shared/project-discovery-dirs.ts +++ b/src/shared/project-discovery-dirs.ts @@ -1,13 +1,29 @@ -import { existsSync } from "node:fs" +import { execFileSync } from "node:child_process" +import { existsSync, realpathSync } from "node:fs" import { dirname, join, resolve } from "node:path" +function normalizePath(path: string): string { + const resolvedPath = resolve(path) + if (!existsSync(resolvedPath)) { + return resolvedPath + } + + try { + return realpathSync(resolvedPath) + } catch { + return resolvedPath + } +} + function findAncestorDirectories( startDirectory: string, targetPaths: ReadonlyArray>, + stopDirectory?: string, ): string[] { const directories: string[] = [] const seen = new Set() - let currentDirectory = resolve(startDirectory) + let currentDirectory = normalizePath(startDirectory) + const resolvedStopDirectory = stopDirectory ? normalizePath(stopDirectory) : undefined while (true) { for (const targetPath of targetPaths) { @@ -20,33 +36,66 @@ function findAncestorDirectories( directories.push(candidateDirectory) } + if (resolvedStopDirectory === currentDirectory) { + return directories + } + const parentDirectory = dirname(currentDirectory) if (parentDirectory === currentDirectory) { return directories } - currentDirectory = parentDirectory + currentDirectory = normalizePath(parentDirectory) } } -export function findProjectClaudeSkillDirs(startDirectory: string): string[] { - return findAncestorDirectories(startDirectory, [[".claude", "skills"]]) +function detectWorktreePath(directory: string): string | undefined { + try { + return execFileSync("git", ["rev-parse", "--show-toplevel"], { + cwd: directory, + encoding: "utf-8", + timeout: 5000, + stdio: ["pipe", "pipe", "pipe"], + }).trim() + } catch { + return undefined + } } -export function findProjectAgentsSkillDirs(startDirectory: string): string[] { - return findAncestorDirectories(startDirectory, [[".agents", "skills"]]) +export function findProjectClaudeSkillDirs(startDirectory: string, stopDirectory?: string): string[] { + return findAncestorDirectories( + startDirectory, + [[".claude", "skills"]], + stopDirectory ?? detectWorktreePath(startDirectory), + ) } -export function findProjectOpencodeSkillDirs(startDirectory: string): string[] { - return findAncestorDirectories(startDirectory, [ - [".opencode", "skills"], - [".opencode", "skill"], - ]) +export function findProjectAgentsSkillDirs(startDirectory: string, stopDirectory?: string): string[] { + return findAncestorDirectories( + startDirectory, + [[".agents", "skills"]], + stopDirectory ?? detectWorktreePath(startDirectory), + ) } -export function findProjectOpencodeCommandDirs(startDirectory: string): string[] { - return findAncestorDirectories(startDirectory, [ - [".opencode", "commands"], - [".opencode", "command"], - ]) +export function findProjectOpencodeSkillDirs(startDirectory: string, stopDirectory?: string): string[] { + return findAncestorDirectories( + startDirectory, + [ + [".opencode", "skills"], + [".opencode", "skill"], + ], + stopDirectory ?? detectWorktreePath(startDirectory), + ) +} + +export function findProjectOpencodeCommandDirs(startDirectory: string, stopDirectory?: string): string[] { + return findAncestorDirectories( + startDirectory, + [ + [".opencode", "commands"], + [".opencode", "command"], + ], + stopDirectory ?? detectWorktreePath(startDirectory), + ) } From e4a5973b16f00d19620e243bae8c81aff797fe89 Mon Sep 17 00:00:00 2001 From: YeonGyu-Kim Date: Thu, 26 Mar 2026 12:15:47 +0900 Subject: [PATCH 35/63] fix(agents): include .agents skills in agent awareness Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus --- .../agent-config-handler.test.ts | 54 +++++++++++++++++++ src/plugin-handlers/agent-config-handler.ts | 10 ++++ 2 files changed, 64 insertions(+) diff --git a/src/plugin-handlers/agent-config-handler.test.ts b/src/plugin-handlers/agent-config-handler.test.ts index bacc3fa24..6cb7514ed 100644 --- a/src/plugin-handlers/agent-config-handler.test.ts +++ b/src/plugin-handlers/agent-config-handler.test.ts @@ -8,6 +8,7 @@ import * as sisyphusJunior from "../agents/sisyphus-junior" import type { OhMyOpenCodeConfig } from "../config" import * as agentLoader from "../features/claude-code-agent-loader" import * as skillLoader from "../features/opencode-skill-loader" +import type { LoadedSkill } from "../features/opencode-skill-loader" import { getAgentDisplayName } from "../shared/agent-display-names" import { applyAgentConfig } from "./agent-config-handler" import type { PluginComponents } from "./plugin-components-loader" @@ -51,6 +52,8 @@ describe("applyAgentConfig builtin override protection", () => { let discoverProjectClaudeSkillsSpy: ReturnType let discoverOpencodeGlobalSkillsSpy: ReturnType let discoverOpencodeProjectSkillsSpy: ReturnType + let discoverProjectAgentsSkillsSpy: ReturnType + let discoverGlobalAgentsSkillsSpy: ReturnType let loadUserAgentsSpy: ReturnType let loadProjectAgentsSpy: ReturnType let migrateAgentConfigSpy: ReturnType @@ -121,6 +124,14 @@ describe("applyAgentConfig builtin override protection", () => { skillLoader, "discoverOpencodeProjectSkills", ).mockResolvedValue([]) + discoverProjectAgentsSkillsSpy = spyOn( + skillLoader, + "discoverProjectAgentsSkills", + ).mockResolvedValue([]) + discoverGlobalAgentsSkillsSpy = spyOn( + skillLoader, + "discoverGlobalAgentsSkills", + ).mockResolvedValue([]) loadUserAgentsSpy = spyOn(agentLoader, "loadUserAgents").mockReturnValue({}) loadProjectAgentsSpy = spyOn(agentLoader, "loadProjectAgents").mockReturnValue({}) @@ -139,6 +150,8 @@ describe("applyAgentConfig builtin override protection", () => { discoverProjectClaudeSkillsSpy.mockRestore() discoverOpencodeGlobalSkillsSpy.mockRestore() discoverOpencodeProjectSkillsSpy.mockRestore() + discoverProjectAgentsSkillsSpy.mockRestore() + discoverGlobalAgentsSkillsSpy.mockRestore() loadUserAgentsSpy.mockRestore() loadProjectAgentsSpy.mockRestore() migrateAgentConfigSpy.mockRestore() @@ -279,4 +292,45 @@ describe("applyAgentConfig builtin override protection", () => { // then expect(createSisyphusJuniorAgentSpy).toHaveBeenCalledWith(undefined, "openai/gpt-5.4", false) }) + + test("includes project and global .agents skills in builtin agent awareness", async () => { + // given + const projectAgentsSkill = { + name: "project-agent-skill", + definition: { + name: "project-agent-skill", + description: "Project agent skill", + template: "template", + }, + scope: "project", + } satisfies LoadedSkill + const globalAgentsSkill = { + name: "global-agent-skill", + definition: { + name: "global-agent-skill", + description: "Global agent skill", + template: "template", + }, + scope: "user", + } satisfies LoadedSkill + discoverProjectAgentsSkillsSpy.mockResolvedValue([projectAgentsSkill]) + discoverGlobalAgentsSkillsSpy.mockResolvedValue([globalAgentsSkill]) + + // when + await applyAgentConfig({ + config: createBaseConfig(), + pluginConfig: createPluginConfig(), + ctx: { directory: "/tmp" }, + pluginComponents: createPluginComponents(), + }) + + // then + const discoveredSkills = createBuiltinAgentsSpy.mock.calls[0]?.[6] + expect(discoveredSkills).toEqual( + expect.arrayContaining([ + expect.objectContaining({ name: "project-agent-skill" }), + expect.objectContaining({ name: "global-agent-skill" }), + ]), + ) + }) }) diff --git a/src/plugin-handlers/agent-config-handler.ts b/src/plugin-handlers/agent-config-handler.ts index 33f15f233..539fa6bb0 100644 --- a/src/plugin-handlers/agent-config-handler.ts +++ b/src/plugin-handlers/agent-config-handler.ts @@ -6,8 +6,10 @@ import { AGENT_NAME_MAP } from "../shared/migration"; import { getAgentDisplayName } from "../shared/agent-display-names"; import { discoverConfigSourceSkills, + discoverGlobalAgentsSkills, discoverOpencodeGlobalSkills, discoverOpencodeProjectSkills, + discoverProjectAgentsSkills, discoverProjectClaudeSkills, discoverUserClaudeSkills, } from "../features/opencode-skill-loader"; @@ -52,8 +54,10 @@ export async function applyAgentConfig(params: { discoveredConfigSourceSkills, discoveredUserSkills, discoveredProjectSkills, + discoveredProjectAgentsSkills, discoveredOpencodeGlobalSkills, discoveredOpencodeProjectSkills, + discoveredGlobalAgentsSkills, ] = await Promise.all([ discoverConfigSourceSkills({ config: params.pluginConfig.skills, @@ -63,16 +67,22 @@ export async function applyAgentConfig(params: { includeClaudeSkillsForAwareness ? discoverProjectClaudeSkills(params.ctx.directory) : Promise.resolve([]), + includeClaudeSkillsForAwareness + ? discoverProjectAgentsSkills(params.ctx.directory) + : Promise.resolve([]), discoverOpencodeGlobalSkills(), discoverOpencodeProjectSkills(params.ctx.directory), + includeClaudeSkillsForAwareness ? discoverGlobalAgentsSkills() : Promise.resolve([]), ]); const allDiscoveredSkills = [ ...discoveredConfigSourceSkills, ...discoveredOpencodeProjectSkills, ...discoveredProjectSkills, + ...discoveredProjectAgentsSkills, ...discoveredOpencodeGlobalSkills, ...discoveredUserSkills, + ...discoveredGlobalAgentsSkills, ]; const browserProvider = From 12a431843924a7135f4809ff71a367c375d6b713 Mon Sep 17 00:00:00 2001 From: YeonGyu-Kim Date: Thu, 26 Mar 2026 12:15:47 +0900 Subject: [PATCH 36/63] fix(commands): load .agents skills into command config Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus --- src/features/opencode-skill-loader/loader.ts | 14 +++ .../command-config-handler.test.ts | 98 +++++++++++++++++++ src/plugin-handlers/command-config-handler.ts | 8 ++ 3 files changed, 120 insertions(+) create mode 100644 src/plugin-handlers/command-config-handler.test.ts diff --git a/src/features/opencode-skill-loader/loader.ts b/src/features/opencode-skill-loader/loader.ts index e577809fe..6f0c44c3b 100644 --- a/src/features/opencode-skill-loader/loader.ts +++ b/src/features/opencode-skill-loader/loader.ts @@ -48,6 +48,20 @@ export async function loadOpencodeProjectSkills(directory?: string): Promise> { + const agentsProjectSkillDirs = findProjectAgentsSkillDirs(directory ?? process.cwd()) + const allSkills = await Promise.all( + agentsProjectSkillDirs.map((skillsDir) => loadSkillsFromDir({ skillsDir, scope: "project" })), + ) + return skillsToCommandDefinitionRecord(deduplicateSkillsByName(allSkills.flat())) +} + +export async function loadGlobalAgentsSkills(): Promise> { + const agentsGlobalDir = join(homedir(), ".agents", "skills") + const skills = await loadSkillsFromDir({ skillsDir: agentsGlobalDir, scope: "user" }) + return skillsToCommandDefinitionRecord(skills) +} + export interface DiscoverSkillsOptions { includeClaudeCodePaths?: boolean directory?: string diff --git a/src/plugin-handlers/command-config-handler.test.ts b/src/plugin-handlers/command-config-handler.test.ts new file mode 100644 index 000000000..7767c6639 --- /dev/null +++ b/src/plugin-handlers/command-config-handler.test.ts @@ -0,0 +1,98 @@ +import { afterEach, beforeEach, describe, expect, spyOn, test } from "bun:test"; +import * as builtinCommands from "../features/builtin-commands"; +import * as commandLoader from "../features/claude-code-command-loader"; +import * as skillLoader from "../features/opencode-skill-loader"; +import type { OhMyOpenCodeConfig } from "../config"; +import type { PluginComponents } from "./plugin-components-loader"; +import { applyCommandConfig } from "./command-config-handler"; + +function createPluginComponents(): PluginComponents { + return { + commands: {}, + skills: {}, + agents: {}, + mcpServers: {}, + hooksConfigs: [], + plugins: [], + errors: [], + }; +} + +function createPluginConfig(): OhMyOpenCodeConfig { + return {}; +} + +describe("applyCommandConfig", () => { + let loadBuiltinCommandsSpy: ReturnType; + let loadUserCommandsSpy: ReturnType; + let loadProjectCommandsSpy: ReturnType; + let loadOpencodeGlobalCommandsSpy: ReturnType; + let loadOpencodeProjectCommandsSpy: ReturnType; + let discoverConfigSourceSkillsSpy: ReturnType; + let loadUserSkillsSpy: ReturnType; + let loadProjectSkillsSpy: ReturnType; + let loadOpencodeGlobalSkillsSpy: ReturnType; + let loadOpencodeProjectSkillsSpy: ReturnType; + let loadProjectAgentsSkillsSpy: ReturnType; + let loadGlobalAgentsSkillsSpy: ReturnType; + + beforeEach(() => { + loadBuiltinCommandsSpy = spyOn(builtinCommands, "loadBuiltinCommands").mockReturnValue({}); + loadUserCommandsSpy = spyOn(commandLoader, "loadUserCommands").mockResolvedValue({}); + loadProjectCommandsSpy = spyOn(commandLoader, "loadProjectCommands").mockResolvedValue({}); + loadOpencodeGlobalCommandsSpy = spyOn(commandLoader, "loadOpencodeGlobalCommands").mockResolvedValue({}); + loadOpencodeProjectCommandsSpy = spyOn(commandLoader, "loadOpencodeProjectCommands").mockResolvedValue({}); + discoverConfigSourceSkillsSpy = spyOn(skillLoader, "discoverConfigSourceSkills").mockResolvedValue([]); + loadUserSkillsSpy = spyOn(skillLoader, "loadUserSkills").mockResolvedValue({}); + loadProjectSkillsSpy = spyOn(skillLoader, "loadProjectSkills").mockResolvedValue({}); + loadOpencodeGlobalSkillsSpy = spyOn(skillLoader, "loadOpencodeGlobalSkills").mockResolvedValue({}); + loadOpencodeProjectSkillsSpy = spyOn(skillLoader, "loadOpencodeProjectSkills").mockResolvedValue({}); + loadProjectAgentsSkillsSpy = spyOn(skillLoader, "loadProjectAgentsSkills").mockResolvedValue({}); + loadGlobalAgentsSkillsSpy = spyOn(skillLoader, "loadGlobalAgentsSkills").mockResolvedValue({}); + }); + + afterEach(() => { + loadBuiltinCommandsSpy.mockRestore(); + loadUserCommandsSpy.mockRestore(); + loadProjectCommandsSpy.mockRestore(); + loadOpencodeGlobalCommandsSpy.mockRestore(); + loadOpencodeProjectCommandsSpy.mockRestore(); + discoverConfigSourceSkillsSpy.mockRestore(); + loadUserSkillsSpy.mockRestore(); + loadProjectSkillsSpy.mockRestore(); + loadOpencodeGlobalSkillsSpy.mockRestore(); + loadOpencodeProjectSkillsSpy.mockRestore(); + loadProjectAgentsSkillsSpy.mockRestore(); + loadGlobalAgentsSkillsSpy.mockRestore(); + }); + + test("includes .agents skills in command config", async () => { + // given + loadProjectAgentsSkillsSpy.mockResolvedValue({ + "agents-project-skill": { + description: "(project - Skill) Agents project skill", + template: "template", + }, + }); + loadGlobalAgentsSkillsSpy.mockResolvedValue({ + "agents-global-skill": { + description: "(user - Skill) Agents global skill", + template: "template", + }, + }); + const config: Record = { command: {} }; + + // when + await applyCommandConfig({ + config, + pluginConfig: createPluginConfig(), + ctx: { directory: "/tmp" }, + pluginComponents: createPluginComponents(), + }); + + // then + const commandConfig = config.command as Record; + expect(commandConfig["agents-project-skill"]?.description).toContain("Agents project skill"); + expect(commandConfig["agents-global-skill"]?.description).toContain("Agents global skill"); + }); +}); diff --git a/src/plugin-handlers/command-config-handler.ts b/src/plugin-handlers/command-config-handler.ts index a5cb0e946..7afd1e416 100644 --- a/src/plugin-handlers/command-config-handler.ts +++ b/src/plugin-handlers/command-config-handler.ts @@ -9,6 +9,8 @@ import { import { loadBuiltinCommands } from "../features/builtin-commands"; import { discoverConfigSourceSkills, + loadGlobalAgentsSkills, + loadProjectAgentsSkills, loadUserSkills, loadProjectSkills, loadOpencodeGlobalSkills, @@ -36,7 +38,9 @@ export async function applyCommandConfig(params: { opencodeGlobalCommands, opencodeProjectCommands, userSkills, + globalAgentsSkills, projectSkills, + projectAgentsSkills, opencodeGlobalSkills, opencodeProjectSkills, ] = await Promise.all([ @@ -49,7 +53,9 @@ export async function applyCommandConfig(params: { loadOpencodeGlobalCommands(), loadOpencodeProjectCommands(params.ctx.directory), includeClaudeSkills ? loadUserSkills() : Promise.resolve({}), + includeClaudeSkills ? loadGlobalAgentsSkills() : Promise.resolve({}), includeClaudeSkills ? loadProjectSkills(params.ctx.directory) : Promise.resolve({}), + includeClaudeSkills ? loadProjectAgentsSkills(params.ctx.directory) : Promise.resolve({}), loadOpencodeGlobalSkills(), loadOpencodeProjectSkills(params.ctx.directory), ]); @@ -59,11 +65,13 @@ export async function applyCommandConfig(params: { ...skillsToCommandDefinitionRecord(configSourceSkills), ...userCommands, ...userSkills, + ...globalAgentsSkills, ...opencodeGlobalCommands, ...opencodeGlobalSkills, ...systemCommands, ...projectCommands, ...projectSkills, + ...projectAgentsSkills, ...opencodeProjectCommands, ...opencodeProjectSkills, ...params.pluginComponents.commands, From b20a34bfa7fe7badf48df239b3a178e25ffb8e10 Mon Sep 17 00:00:00 2001 From: YeonGyu-Kim Date: Thu, 26 Mar 2026 12:15:47 +0900 Subject: [PATCH 37/63] fix(slashcommand): discover nested opencode commands Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus --- .../slashcommand/command-discovery.test.ts | 20 +++++++++++++++++++ src/tools/slashcommand/command-discovery.ts | 18 +++++++++++++++-- 2 files changed, 36 insertions(+), 2 deletions(-) diff --git a/src/tools/slashcommand/command-discovery.test.ts b/src/tools/slashcommand/command-discovery.test.ts index 232515a33..1bf6ce016 100644 --- a/src/tools/slashcommand/command-discovery.test.ts +++ b/src/tools/slashcommand/command-discovery.test.ts @@ -235,4 +235,24 @@ Use plural command. expect(duplicates).toHaveLength(1) expect(duplicates[0]?.content).toContain("Use plural command.") }) + + it("discovers nested opencode project commands", () => { + const commandsDir = join(projectDir, ".opencode", "commands", "refactor") + + mkdirSync(commandsDir, { recursive: true }) + writeFileSync( + join(commandsDir, "code.md"), + `--- +description: Nested command +--- +Use nested command. +`, + ) + + const commands = discoverCommandsSync(projectDir) + const nestedCommand = commands.find((command) => command.name === "refactor:code") + + expect(nestedCommand?.content).toContain("Use nested command.") + expect(nestedCommand?.scope).toBe("opencode-project") + }) }) diff --git a/src/tools/slashcommand/command-discovery.ts b/src/tools/slashcommand/command-discovery.ts index 7567e74dd..574c0cb65 100644 --- a/src/tools/slashcommand/command-discovery.ts +++ b/src/tools/slashcommand/command-discovery.ts @@ -18,17 +18,31 @@ export interface CommandDiscoveryOptions { enabledPluginsOverride?: Record } -function discoverCommandsFromDir(commandsDir: string, scope: CommandScope): CommandInfo[] { +function discoverCommandsFromDir( + commandsDir: string, + scope: CommandScope, + prefix = "", +): CommandInfo[] { if (!existsSync(commandsDir)) return [] const entries = readdirSync(commandsDir, { withFileTypes: true }) const commands: CommandInfo[] = [] for (const entry of entries) { + if (entry.isDirectory()) { + if (entry.name.startsWith(".")) continue + const nestedPrefix = prefix ? `${prefix}:${entry.name}` : entry.name + commands.push( + ...discoverCommandsFromDir(join(commandsDir, entry.name), scope, nestedPrefix), + ) + continue + } + if (!isMarkdownFile(entry)) continue const commandPath = join(commandsDir, entry.name) - const commandName = basename(entry.name, ".md") + const baseCommandName = basename(entry.name, ".md") + const commandName = prefix ? `${prefix}:${baseCommandName}` : baseCommandName try { const content = readFileSync(commandPath, "utf-8") From 7f742723b5d75501acbbbda452d398e7b5bf6ecb Mon Sep 17 00:00:00 2001 From: YeonGyu-Kim Date: Thu, 26 Mar 2026 12:29:42 +0900 Subject: [PATCH 38/63] fix(slashcommand): use slash separator for nested commands Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus --- src/tools/slashcommand/command-discovery.test.ts | 2 +- src/tools/slashcommand/command-discovery.ts | 10 ++++++++-- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/src/tools/slashcommand/command-discovery.test.ts b/src/tools/slashcommand/command-discovery.test.ts index 1bf6ce016..b0b3c2b5a 100644 --- a/src/tools/slashcommand/command-discovery.test.ts +++ b/src/tools/slashcommand/command-discovery.test.ts @@ -250,7 +250,7 @@ Use nested command. ) const commands = discoverCommandsSync(projectDir) - const nestedCommand = commands.find((command) => command.name === "refactor:code") + const nestedCommand = commands.find((command) => command.name === "refactor/code") expect(nestedCommand?.content).toContain("Use nested command.") expect(nestedCommand?.scope).toBe("opencode-project") diff --git a/src/tools/slashcommand/command-discovery.ts b/src/tools/slashcommand/command-discovery.ts index 574c0cb65..dc8922381 100644 --- a/src/tools/slashcommand/command-discovery.ts +++ b/src/tools/slashcommand/command-discovery.ts @@ -18,6 +18,8 @@ export interface CommandDiscoveryOptions { enabledPluginsOverride?: Record } +const NESTED_COMMAND_SEPARATOR = "/" + function discoverCommandsFromDir( commandsDir: string, scope: CommandScope, @@ -31,7 +33,9 @@ function discoverCommandsFromDir( for (const entry of entries) { if (entry.isDirectory()) { if (entry.name.startsWith(".")) continue - const nestedPrefix = prefix ? `${prefix}:${entry.name}` : entry.name + const nestedPrefix = prefix + ? `${prefix}${NESTED_COMMAND_SEPARATOR}${entry.name}` + : entry.name commands.push( ...discoverCommandsFromDir(join(commandsDir, entry.name), scope, nestedPrefix), ) @@ -42,7 +46,9 @@ function discoverCommandsFromDir( const commandPath = join(commandsDir, entry.name) const baseCommandName = basename(entry.name, ".md") - const commandName = prefix ? `${prefix}:${baseCommandName}` : baseCommandName + const commandName = prefix + ? `${prefix}${NESTED_COMMAND_SEPARATOR}${baseCommandName}` + : baseCommandName try { const content = readFileSync(commandPath, "utf-8") From 19838b78a7994dc27c295c94b7315136c7111589 Mon Sep 17 00:00:00 2001 From: YeonGyu-Kim Date: Thu, 26 Mar 2026 12:58:57 +0900 Subject: [PATCH 39/63] fix(shared): add bounded project discovery helpers Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus --- src/shared/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/shared/index.ts b/src/shared/index.ts index 5d2615d70..ee690e816 100644 --- a/src/shared/index.ts +++ b/src/shared/index.ts @@ -62,11 +62,11 @@ export * from "./truncate-description" export * from "./opencode-storage-paths" export * from "./opencode-message-dir" export * from "./opencode-command-dirs" +export * from "./project-discovery-dirs" export * from "./normalize-sdk-response" export * from "./session-directory-resolver" export * from "./prompt-tools" export * from "./internal-initiator-marker" export * from "./plugin-command-discovery" -export * from "./project-discovery-dirs" export { SessionCategoryRegistry } from "./session-category-registry" export * from "./plugin-identity" From 961cc788f6ade53ff51bce52b03fb9fc7552fa6e Mon Sep 17 00:00:00 2001 From: YeonGyu-Kim Date: Thu, 26 Mar 2026 12:59:05 +0900 Subject: [PATCH 40/63] fix(shared): support opencode directory aliases Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus --- src/shared/opencode-command-dirs.test.ts | 2 +- src/shared/opencode-command-dirs.ts | 2 -- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/src/shared/opencode-command-dirs.test.ts b/src/shared/opencode-command-dirs.test.ts index 4b2ac48f0..e75b0284c 100644 --- a/src/shared/opencode-command-dirs.test.ts +++ b/src/shared/opencode-command-dirs.test.ts @@ -27,8 +27,8 @@ describe("opencode-command-dirs", () => { expect(dirs).toContain("/home/user/.config/opencode/profiles/opus/skills") expect(dirs).toContain("/home/user/.config/opencode/profiles/opus/skill") - expect(dirs).toContain("/home/user/.config/opencode/skills") expect(dirs).toContain("/home/user/.config/opencode/skill") + expect(dirs).toContain("/home/user/.config/opencode/skills") expect(dirs).toHaveLength(4) }) }) diff --git a/src/shared/opencode-command-dirs.ts b/src/shared/opencode-command-dirs.ts index 4431370ad..34f2ecce1 100644 --- a/src/shared/opencode-command-dirs.ts +++ b/src/shared/opencode-command-dirs.ts @@ -14,7 +14,6 @@ function getParentOpencodeConfigDir(configDir: string): string | null { export function getOpenCodeCommandDirs(options: OpenCodeConfigDirOptions): string[] { const configDir = getOpenCodeConfigDir(options) const parentConfigDir = getParentOpencodeConfigDir(configDir) - return Array.from( new Set([ join(configDir, "commands"), @@ -27,7 +26,6 @@ export function getOpenCodeCommandDirs(options: OpenCodeConfigDirOptions): strin export function getOpenCodeSkillDirs(options: OpenCodeConfigDirOptions): string[] { const configDir = getOpenCodeConfigDir(options) const parentConfigDir = getParentOpencodeConfigDir(configDir) - return Array.from( new Set([ join(configDir, "skills"), From 86a62aef45aae2a4627b07ebc02ebf42eeec0db7 Mon Sep 17 00:00:00 2001 From: YeonGyu-Kim Date: Thu, 26 Mar 2026 12:59:13 +0900 Subject: [PATCH 41/63] fix(skills): discover ancestor project skill directories Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus --- .../project-skill-discovery.test.ts | 86 +++++++++++++++++++ 1 file changed, 86 insertions(+) create mode 100644 src/features/opencode-skill-loader/project-skill-discovery.test.ts diff --git a/src/features/opencode-skill-loader/project-skill-discovery.test.ts b/src/features/opencode-skill-loader/project-skill-discovery.test.ts new file mode 100644 index 000000000..0d34da8ea --- /dev/null +++ b/src/features/opencode-skill-loader/project-skill-discovery.test.ts @@ -0,0 +1,86 @@ +import { execFileSync } from "node:child_process" +import { afterEach, beforeEach, describe, expect, it } from "bun:test" +import { mkdtempSync, mkdirSync, rmSync, writeFileSync } from "node:fs" +import { tmpdir } from "node:os" +import { join } from "node:path" +import { + discoverOpencodeProjectSkills, + discoverProjectAgentsSkills, + discoverProjectClaudeSkills, +} from "./loader" + +function writeSkill(directory: string, name: string, description: string): void { + mkdirSync(directory, { recursive: true }) + writeFileSync( + join(directory, "SKILL.md"), + `---\nname: ${name}\ndescription: ${description}\n---\nBody\n`, + ) +} + +describe("project skill discovery", () => { + let tempDir = "" + + beforeEach(() => { + tempDir = mkdtempSync(join(tmpdir(), "omo-project-skill-discovery-")) + }) + + afterEach(() => { + rmSync(tempDir, { recursive: true, force: true }) + }) + + it("discovers ancestor project skill directories up to the worktree root", async () => { + // given + const repositoryDir = join(tempDir, "repo") + const nestedDirectory = join(repositoryDir, "packages", "app", "src") + + mkdirSync(nestedDirectory, { recursive: true }) + execFileSync("git", ["init"], { + cwd: repositoryDir, + stdio: ["ignore", "ignore", "ignore"], + }) + + writeSkill( + join(repositoryDir, ".claude", "skills", "repo-claude"), + "repo-claude", + "Discovered from the repository root", + ) + writeSkill( + join(repositoryDir, ".agents", "skills", "repo-agents"), + "repo-agents", + "Discovered from the repository root", + ) + writeSkill( + join(repositoryDir, ".opencode", "skill", "repo-opencode"), + "repo-opencode", + "Discovered from the repository root", + ) + + writeSkill( + join(tempDir, ".claude", "skills", "outside-claude"), + "outside-claude", + "Should stay outside the worktree", + ) + writeSkill( + join(tempDir, ".agents", "skills", "outside-agents"), + "outside-agents", + "Should stay outside the worktree", + ) + writeSkill( + join(tempDir, ".opencode", "skills", "outside-opencode"), + "outside-opencode", + "Should stay outside the worktree", + ) + + // when + const [claudeSkills, agentSkills, opencodeSkills] = await Promise.all([ + discoverProjectClaudeSkills(nestedDirectory), + discoverProjectAgentsSkills(nestedDirectory), + discoverOpencodeProjectSkills(nestedDirectory), + ]) + + // then + expect(claudeSkills.map(skill => skill.name)).toEqual(["repo-claude"]) + expect(agentSkills.map(skill => skill.name)).toEqual(["repo-agents"]) + expect(opencodeSkills.map(skill => skill.name)).toEqual(["repo-opencode"]) + }) +}) From 8413bc6a91c87bb394ad622a136307c8d2a78db0 Mon Sep 17 00:00:00 2001 From: YeonGyu-Kim Date: Thu, 26 Mar 2026 12:59:19 +0900 Subject: [PATCH 42/63] fix(skills): expand tilde config source paths Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus --- .../config-source-discovery.test.ts | 26 +++++++++++++++++++ .../config-source-discovery.ts | 1 + 2 files changed, 27 insertions(+) diff --git a/src/features/opencode-skill-loader/config-source-discovery.test.ts b/src/features/opencode-skill-loader/config-source-discovery.test.ts index 091118ce6..550a3a195 100644 --- a/src/features/opencode-skill-loader/config-source-discovery.test.ts +++ b/src/features/opencode-skill-loader/config-source-discovery.test.ts @@ -1,5 +1,6 @@ import { afterEach, beforeEach, describe, expect, it } from "bun:test" import { mkdirSync, rmSync, writeFileSync } from "fs" +import { homedir } from "os" import { join } from "path" import { homedir, tmpdir } from "os" import { SkillsConfigSchema } from "../../config/schema/skills" @@ -101,4 +102,29 @@ describe("config source discovery", () => { // then expect(normalized).toBe("keep/nested/SKILL.md") }) + + it("loads skills from ~/ sources paths", async () => { + // given + const homeTestDir = join(homedir(), `.omo-config-source-${Date.now()}`) + const sourceDir = join(homeTestDir, "custom-skills") + writeSkill(join(sourceDir, "tilde-skill"), "tilde-skill", "Loaded from tilde source") + const config = SkillsConfigSchema.parse({ + sources: [{ path: `${homeTestDir.replace(homedir(), "~")}/custom-skills`, recursive: true }], + }) + + try { + // when + const skills = await discoverConfigSourceSkills({ + config, + configDir: join(TEST_DIR, "config"), + }) + + // then + const tildeSkill = skills.find((skill) => skill.name === "tilde-skill") + expect(tildeSkill).toBeDefined() + expect(tildeSkill?.definition.description).toContain("Loaded from tilde source") + } finally { + rmSync(homeTestDir, { recursive: true, force: true }) + } + }) }) diff --git a/src/features/opencode-skill-loader/config-source-discovery.ts b/src/features/opencode-skill-loader/config-source-discovery.ts index b290c8b30..ef3b9a8a6 100644 --- a/src/features/opencode-skill-loader/config-source-discovery.ts +++ b/src/features/opencode-skill-loader/config-source-discovery.ts @@ -1,6 +1,7 @@ import { promises as fs } from "fs" import { homedir } from "os" import { dirname, extname, isAbsolute, join, relative } from "path" +import { homedir } from "os" import picomatch from "picomatch" import type { SkillsConfig } from "../../config/schema" import { normalizeSkillsConfig } from "./merger/skills-config-normalizer" From 4c8aacef48729d75006a7225de0b398dec76e89e Mon Sep 17 00:00:00 2001 From: YeonGyu-Kim Date: Thu, 26 Mar 2026 12:59:27 +0900 Subject: [PATCH 43/63] fix(agents): include .agents skills in agent awareness Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus --- ...agent-config-handler-agents-skills.test.ts | 125 ++++++++++++++++++ 1 file changed, 125 insertions(+) create mode 100644 src/plugin-handlers/agent-config-handler-agents-skills.test.ts diff --git a/src/plugin-handlers/agent-config-handler-agents-skills.test.ts b/src/plugin-handlers/agent-config-handler-agents-skills.test.ts new file mode 100644 index 000000000..593f22d9e --- /dev/null +++ b/src/plugin-handlers/agent-config-handler-agents-skills.test.ts @@ -0,0 +1,125 @@ +import type { AgentConfig } from "@opencode-ai/sdk" +import { afterEach, beforeEach, describe, expect, spyOn, test } from "bun:test" +import * as agents from "../agents" +import * as shared from "../shared" +import * as sisyphusJunior from "../agents/sisyphus-junior" +import type { OhMyOpenCodeConfig } from "../config" +import * as skillLoader from "../features/opencode-skill-loader" +import { applyAgentConfig } from "./agent-config-handler" +import type { PluginComponents } from "./plugin-components-loader" + +function createPluginComponents(): PluginComponents { + return { + commands: {}, + skills: {}, + agents: {}, + mcpServers: {}, + hooksConfigs: [], + plugins: [], + errors: [], + } +} + +function createPluginConfig(): OhMyOpenCodeConfig { + return { + sisyphus_agent: { + planner_enabled: false, + }, + } +} + +describe("applyAgentConfig .agents skills", () => { + let createBuiltinAgentsSpy: ReturnType + let createSisyphusJuniorAgentSpy: ReturnType + let discoverConfigSourceSkillsSpy: ReturnType + let discoverUserClaudeSkillsSpy: ReturnType + let discoverProjectClaudeSkillsSpy: ReturnType + let discoverOpencodeGlobalSkillsSpy: ReturnType + let discoverOpencodeProjectSkillsSpy: ReturnType + let discoverProjectAgentsSkillsSpy: ReturnType + let discoverGlobalAgentsSkillsSpy: ReturnType + let logSpy: ReturnType + + beforeEach(() => { + createBuiltinAgentsSpy = spyOn(agents, "createBuiltinAgents").mockResolvedValue({ + sisyphus: { name: "sisyphus", prompt: "builtin", mode: "primary" } satisfies AgentConfig, + }) + createSisyphusJuniorAgentSpy = spyOn( + sisyphusJunior, + "createSisyphusJuniorAgentWithOverrides", + ).mockReturnValue({ + name: "sisyphus-junior", + prompt: "junior", + mode: "all", + } satisfies AgentConfig) + discoverConfigSourceSkillsSpy = spyOn(skillLoader, "discoverConfigSourceSkills").mockResolvedValue([]) + discoverUserClaudeSkillsSpy = spyOn(skillLoader, "discoverUserClaudeSkills").mockResolvedValue([]) + discoverProjectClaudeSkillsSpy = spyOn(skillLoader, "discoverProjectClaudeSkills").mockResolvedValue([]) + discoverOpencodeGlobalSkillsSpy = spyOn(skillLoader, "discoverOpencodeGlobalSkills").mockResolvedValue([]) + discoverOpencodeProjectSkillsSpy = spyOn(skillLoader, "discoverOpencodeProjectSkills").mockResolvedValue([]) + discoverProjectAgentsSkillsSpy = spyOn(skillLoader, "discoverProjectAgentsSkills").mockResolvedValue([]) + discoverGlobalAgentsSkillsSpy = spyOn(skillLoader, "discoverGlobalAgentsSkills").mockResolvedValue([]) + logSpy = spyOn(shared, "log").mockImplementation(() => {}) + }) + + afterEach(() => { + createBuiltinAgentsSpy.mockRestore() + createSisyphusJuniorAgentSpy.mockRestore() + discoverConfigSourceSkillsSpy.mockRestore() + discoverUserClaudeSkillsSpy.mockRestore() + discoverProjectClaudeSkillsSpy.mockRestore() + discoverOpencodeGlobalSkillsSpy.mockRestore() + discoverOpencodeProjectSkillsSpy.mockRestore() + discoverProjectAgentsSkillsSpy.mockRestore() + discoverGlobalAgentsSkillsSpy.mockRestore() + logSpy.mockRestore() + }) + + test("calls .agents skill discovery during agent configuration", async () => { + // given + const directory = "/tmp/project" + + // when + await applyAgentConfig({ + config: { model: "anthropic/claude-opus-4-6", agent: {} }, + pluginConfig: createPluginConfig(), + ctx: { directory }, + pluginComponents: createPluginComponents(), + }) + + // then + expect(discoverProjectAgentsSkillsSpy).toHaveBeenCalledWith(directory) + expect(discoverGlobalAgentsSkillsSpy).toHaveBeenCalled() + }) + + test("passes discovered .agents skills to builtin agent creation", async () => { + // given + discoverProjectAgentsSkillsSpy.mockResolvedValue([ + { + name: "project-agent-skill", + definition: { name: "project-agent-skill", template: "project-template" }, + scope: "project", + }, + ]) + discoverGlobalAgentsSkillsSpy.mockResolvedValue([ + { + name: "global-agent-skill", + definition: { name: "global-agent-skill", template: "global-template" }, + scope: "user", + }, + ]) + + // when + await applyAgentConfig({ + config: { model: "anthropic/claude-opus-4-6", agent: {} }, + pluginConfig: createPluginConfig(), + ctx: { directory: "/tmp/project" }, + pluginComponents: createPluginComponents(), + }) + + // then + const discoveredSkills = createBuiltinAgentsSpy.mock.calls[0]?.[6] as Array<{ name: string }> + expect(discoveredSkills.map(skill => skill.name)).toContain("project-agent-skill") + expect(discoveredSkills.map(skill => skill.name)).toContain("global-agent-skill") + }) +}) From c637d77965f90883c672413283963862e5d68c3d Mon Sep 17 00:00:00 2001 From: YeonGyu-Kim Date: Thu, 26 Mar 2026 12:59:39 +0900 Subject: [PATCH 44/63] fix(commands): discover ancestor opencode project commands Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus --- .../claude-code-command-loader/loader.test.ts | 24 +++++++++++++++++++ .../claude-code-command-loader/loader.ts | 5 ++-- 2 files changed, 26 insertions(+), 3 deletions(-) diff --git a/src/features/claude-code-command-loader/loader.test.ts b/src/features/claude-code-command-loader/loader.test.ts index dde2096c0..be7928d3f 100644 --- a/src/features/claude-code-command-loader/loader.test.ts +++ b/src/features/claude-code-command-loader/loader.test.ts @@ -1,3 +1,4 @@ +import { execFileSync } from "node:child_process" import { afterEach, beforeEach, describe, expect, it } from "bun:test" import { mkdirSync, rmSync, writeFileSync } from "node:fs" import { tmpdir } from "node:os" @@ -98,4 +99,27 @@ describe("claude-code command loader", () => { // then expect(commands["duplicate-global"]?.description).toBe("(opencode) Profile global command") }) + + it("#given nested project opencode commands in a worktree #when loadOpencodeProjectCommands is called #then it preserves slash names and stops at the worktree root", async () => { + // given + const repositoryDir = join(TEST_DIR, "repo") + const nestedDirectory = join(repositoryDir, "packages", "app", "src") + mkdirSync(nestedDirectory, { recursive: true }) + execFileSync("git", ["init"], { + cwd: repositoryDir, + stdio: ["ignore", "ignore", "ignore"], + }) + writeCommand(join(repositoryDir, ".opencode", "commands", "deploy"), "staging", "Deploy staging") + writeCommand(join(repositoryDir, ".opencode", "command"), "release", "Release command") + writeCommand(join(TEST_DIR, ".opencode", "commands"), "outside", "Outside command") + + // when + const commands = await loadOpencodeProjectCommands(nestedDirectory) + + // then + expect(commands["deploy/staging"]?.description).toBe("(opencode-project) Deploy staging") + expect(commands.release?.description).toBe("(opencode-project) Release command") + expect(commands.outside).toBeUndefined() + expect(commands["deploy:staging"]).toBeUndefined() + }) }) diff --git a/src/features/claude-code-command-loader/loader.ts b/src/features/claude-code-command-loader/loader.ts index aee1f6e59..b052f56bd 100644 --- a/src/features/claude-code-command-loader/loader.ts +++ b/src/features/claude-code-command-loader/loader.ts @@ -7,7 +7,6 @@ import { findProjectOpencodeCommandDirs, getClaudeConfigDir, getOpenCodeCommandDirs, - getOpenCodeConfigDir, } from "../../shared" import { log } from "../../shared/logger" import type { CommandScope, CommandDefinition, CommandFrontmatter, LoadedCommand } from "./types" @@ -51,7 +50,7 @@ async function loadCommandsFromDir( if (entry.isDirectory()) { if (entry.name.startsWith(".")) continue const subDirPath = join(commandsDir, entry.name) - const subPrefix = prefix ? `${prefix}:${entry.name}` : entry.name + const subPrefix = prefix ? `${prefix}/${entry.name}` : entry.name const subCommands = await loadCommandsFromDir(subDirPath, scope, visited, subPrefix) commands.push(...subCommands) continue @@ -61,7 +60,7 @@ async function loadCommandsFromDir( const commandPath = join(commandsDir, entry.name) const baseCommandName = basename(entry.name, ".md") - const commandName = prefix ? `${prefix}:${baseCommandName}` : baseCommandName + const commandName = prefix ? `${prefix}/${baseCommandName}` : baseCommandName try { const content = await fs.readFile(commandPath, "utf-8") From 097e2be7e823b972d3d1a44f57bfc731882f0a74 Mon Sep 17 00:00:00 2001 From: YeonGyu-Kim Date: Thu, 26 Mar 2026 12:59:46 +0900 Subject: [PATCH 45/63] fix(slashcommand): discover nested opencode commands with slash names Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus --- ...opencode-project-command-discovery.test.ts | 60 +++++++++++++++++++ 1 file changed, 60 insertions(+) create mode 100644 src/tools/slashcommand/opencode-project-command-discovery.test.ts diff --git a/src/tools/slashcommand/opencode-project-command-discovery.test.ts b/src/tools/slashcommand/opencode-project-command-discovery.test.ts new file mode 100644 index 000000000..f845d9b93 --- /dev/null +++ b/src/tools/slashcommand/opencode-project-command-discovery.test.ts @@ -0,0 +1,60 @@ +import { execFileSync } from "node:child_process" +import { afterEach, beforeEach, describe, expect, it } from "bun:test" +import { mkdtempSync, mkdirSync, rmSync, writeFileSync } from "node:fs" +import { tmpdir } from "node:os" +import { join } from "node:path" +import { discoverCommandsSync } from "./command-discovery" + +function writeCommand(path: string, description: string, body: string): void { + mkdirSync(join(path, ".."), { recursive: true }) + writeFileSync(path, `---\ndescription: ${description}\n---\n${body}\n`) +} + +describe("opencode project command discovery", () => { + let tempDir = "" + + beforeEach(() => { + tempDir = mkdtempSync(join(tmpdir(), "omo-opencode-project-command-discovery-")) + }) + + afterEach(() => { + rmSync(tempDir, { recursive: true, force: true }) + }) + + it("discovers ancestor opencode commands with slash-separated nested names and worktree boundaries", () => { + // given + const repositoryDir = join(tempDir, "repo") + const nestedDirectory = join(repositoryDir, "packages", "app", "src") + + mkdirSync(nestedDirectory, { recursive: true }) + execFileSync("git", ["init"], { + cwd: repositoryDir, + stdio: ["ignore", "ignore", "ignore"], + }) + + writeCommand( + join(repositoryDir, ".opencode", "commands", "deploy", "staging.md"), + "Deploy to staging", + "Run the staged deploy.", + ) + writeCommand( + join(repositoryDir, ".opencode", "command", "release.md"), + "Release command", + "Run the release.", + ) + writeCommand( + join(tempDir, ".opencode", "commands", "outside.md"), + "Outside command", + "Should not be discovered.", + ) + + // when + const names = discoverCommandsSync(nestedDirectory).map(command => command.name) + + // then + expect(names).toContain("deploy/staging") + expect(names).toContain("release") + expect(names).not.toContain("deploy:staging") + expect(names).not.toContain("outside") + }) +}) From 5bc019eb7c1beadb12dab53db7765a68d028969e Mon Sep 17 00:00:00 2001 From: YeonGyu-Kim Date: Thu, 26 Mar 2026 13:06:32 +0900 Subject: [PATCH 46/63] fix(skills): remove duplicate homedir import Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus --- src/features/opencode-skill-loader/config-source-discovery.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/features/opencode-skill-loader/config-source-discovery.ts b/src/features/opencode-skill-loader/config-source-discovery.ts index ef3b9a8a6..b290c8b30 100644 --- a/src/features/opencode-skill-loader/config-source-discovery.ts +++ b/src/features/opencode-skill-loader/config-source-discovery.ts @@ -1,7 +1,6 @@ import { promises as fs } from "fs" import { homedir } from "os" import { dirname, extname, isAbsolute, join, relative } from "path" -import { homedir } from "os" import picomatch from "picomatch" import type { SkillsConfig } from "../../config/schema" import { normalizeSkillsConfig } from "./merger/skills-config-normalizer" From 42f5386100fe540532fc08f1f82638d95e8d8f33 Mon Sep 17 00:00:00 2001 From: YeonGyu-Kim Date: Thu, 26 Mar 2026 13:08:53 +0900 Subject: [PATCH 47/63] fix(tests): drop duplicate tilde config regression Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus --- .../config-source-discovery.test.ts | 26 ------------------- 1 file changed, 26 deletions(-) diff --git a/src/features/opencode-skill-loader/config-source-discovery.test.ts b/src/features/opencode-skill-loader/config-source-discovery.test.ts index 550a3a195..091118ce6 100644 --- a/src/features/opencode-skill-loader/config-source-discovery.test.ts +++ b/src/features/opencode-skill-loader/config-source-discovery.test.ts @@ -1,6 +1,5 @@ import { afterEach, beforeEach, describe, expect, it } from "bun:test" import { mkdirSync, rmSync, writeFileSync } from "fs" -import { homedir } from "os" import { join } from "path" import { homedir, tmpdir } from "os" import { SkillsConfigSchema } from "../../config/schema/skills" @@ -102,29 +101,4 @@ describe("config source discovery", () => { // then expect(normalized).toBe("keep/nested/SKILL.md") }) - - it("loads skills from ~/ sources paths", async () => { - // given - const homeTestDir = join(homedir(), `.omo-config-source-${Date.now()}`) - const sourceDir = join(homeTestDir, "custom-skills") - writeSkill(join(sourceDir, "tilde-skill"), "tilde-skill", "Loaded from tilde source") - const config = SkillsConfigSchema.parse({ - sources: [{ path: `${homeTestDir.replace(homedir(), "~")}/custom-skills`, recursive: true }], - }) - - try { - // when - const skills = await discoverConfigSourceSkills({ - config, - configDir: join(TEST_DIR, "config"), - }) - - // then - const tildeSkill = skills.find((skill) => skill.name === "tilde-skill") - expect(tildeSkill).toBeDefined() - expect(tildeSkill?.definition.description).toContain("Loaded from tilde source") - } finally { - rmSync(homeTestDir, { recursive: true, force: true }) - } - }) }) From dd85d1451a1b8eda5a59bd87dedbda714fa23587 Mon Sep 17 00:00:00 2001 From: YeonGyu-Kim Date: Thu, 26 Mar 2026 15:59:53 +0900 Subject: [PATCH 48/63] fix(model-requirements): align fallback models with available provider catalogs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - opencode/minimax-m2.7-highspeed → opencode/minimax-m2.5 (provider lacks m2.7 variants) - opencode-go/minimax-m2.7-highspeed → opencode-go/minimax-m2.7 (provider lacks -highspeed) - opencode/minimax-m2.7 → opencode/minimax-m2.5 (provider only has m2.5) - added xai as alternative provider for grok-code-fast-1 (prevents wrong provider prefix) --- src/shared/model-requirements.test.ts | 8 ++++---- src/shared/model-requirements.ts | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/shared/model-requirements.test.ts b/src/shared/model-requirements.test.ts index d09a4530a..470dfea3a 100644 --- a/src/shared/model-requirements.test.ts +++ b/src/shared/model-requirements.test.ts @@ -80,7 +80,7 @@ describe("AGENT_MODEL_REQUIREMENTS", () => { const second = librarian.fallbackChain[1] expect(second.providers[0]).toBe("opencode") - expect(second.model).toBe("minimax-m2.7-highspeed") + expect(second.model).toBe("minimax-m2.5") const tertiary = librarian.fallbackChain[2] expect(tertiary.providers).toContain("anthropic") @@ -95,22 +95,22 @@ describe("AGENT_MODEL_REQUIREMENTS", () => { const explore = AGENT_MODEL_REQUIREMENTS["explore"] // when - accessing explore requirement - // then - fallbackChain: grok → minimax-m2.7-highspeed → minimax-m2.7 → haiku → nano expect(explore).toBeDefined() expect(explore.fallbackChain).toBeArray() expect(explore.fallbackChain).toHaveLength(5) const primary = explore.fallbackChain[0] expect(primary.providers).toContain("github-copilot") + expect(primary.providers).toContain("xai") expect(primary.model).toBe("grok-code-fast-1") const secondary = explore.fallbackChain[1] expect(secondary.providers).toContain("opencode-go") - expect(secondary.model).toBe("minimax-m2.7-highspeed") + expect(secondary.model).toBe("minimax-m2.7") const tertiary = explore.fallbackChain[2] expect(tertiary.providers).toContain("opencode") - expect(tertiary.model).toBe("minimax-m2.7") + expect(tertiary.model).toBe("minimax-m2.5") const quaternary = explore.fallbackChain[3] expect(quaternary.providers).toContain("anthropic") diff --git a/src/shared/model-requirements.ts b/src/shared/model-requirements.ts index 541c26d00..aeb1fc629 100644 --- a/src/shared/model-requirements.ts +++ b/src/shared/model-requirements.ts @@ -78,16 +78,16 @@ export const AGENT_MODEL_REQUIREMENTS: Record = { librarian: { fallbackChain: [ { providers: ["opencode-go"], model: "minimax-m2.7" }, - { providers: ["opencode"], model: "minimax-m2.7-highspeed" }, + { providers: ["opencode"], model: "minimax-m2.5" }, { providers: ["anthropic", "opencode"], model: "claude-haiku-4-5" }, { providers: ["opencode"], model: "gpt-5-nano" }, ], }, explore: { fallbackChain: [ - { providers: ["github-copilot"], model: "grok-code-fast-1" }, - { providers: ["opencode-go"], model: "minimax-m2.7-highspeed" }, - { providers: ["opencode"], model: "minimax-m2.7" }, + { providers: ["github-copilot", "xai"], model: "grok-code-fast-1" }, + { providers: ["opencode-go"], model: "minimax-m2.7" }, + { providers: ["opencode"], model: "minimax-m2.5" }, { providers: ["anthropic", "opencode"], model: "claude-haiku-4-5" }, { providers: ["opencode"], model: "gpt-5-nano" }, ], From a8ec92748c19faa2fac82c59c756aa61f2396045 Mon Sep 17 00:00:00 2001 From: YeonGyu-Kim Date: Thu, 26 Mar 2026 16:54:04 +0900 Subject: [PATCH 49/63] fix(model-resolution): honor user config overrides on cold cache MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When provider-models cache is cold (first run / cache miss), resolveModelForDelegateTask returns {skipped: true}. Previously this caused the subagent resolver to: 1. Ignore the user's explicit model override (e.g. explore.model) 2. Fall through to the hardcoded fallback chain which may contain model IDs that don't exist in the provider catalog Now: - subagent-resolver: if resolution is skipped but user explicitly configured a model, use it directly - subagent-resolver: don't assign hardcoded fallback chain on skip - category-resolver: same — don't leak hardcoded chain on skip - general-agents: if user model fails resolution, use it as-is instead of falling back to hardcoded chain first entry Closes #2820 --- src/agents/builtin-agents/general-agents.ts | 8 ++++++-- src/tools/delegate-task/category-resolver.ts | 3 ++- src/tools/delegate-task/subagent-resolver.ts | 20 ++++++++++++++++++-- 3 files changed, 26 insertions(+), 5 deletions(-) diff --git a/src/agents/builtin-agents/general-agents.ts b/src/agents/builtin-agents/general-agents.ts index f5fd1d920..7d9d52979 100644 --- a/src/agents/builtin-agents/general-agents.ts +++ b/src/agents/builtin-agents/general-agents.ts @@ -78,12 +78,16 @@ export function collectPendingBuiltinAgents(input: { }) if (!resolution) { if (override?.model) { - log("[agent-registration] User-configured model could not be resolved, falling back", { + // User explicitly configured a model but resolution failed (e.g., cold cache). + // Honor the user's choice directly instead of falling back to hardcoded chain. + log("[agent-registration] User-configured model not resolved, using as-is", { agent: agentName, configuredModel: override.model, }) + resolution = { model: override.model, provenance: "override" as const } + } else { + resolution = getFirstFallbackModel(requirement) } - resolution = getFirstFallbackModel(requirement) } if (!resolution) continue const { model, variant: resolvedVariant } = resolution diff --git a/src/tools/delegate-task/category-resolver.ts b/src/tools/delegate-task/category-resolver.ts index 648fa933c..8406652f1 100644 --- a/src/tools/delegate-task/category-resolver.ts +++ b/src/tools/delegate-task/category-resolver.ts @@ -239,6 +239,7 @@ Available categories: ${categoryNames.join(", ")}`, modelInfo, actualModel, isUnstableAgent, - fallbackChain: configuredFallbackChain ?? requirement?.fallbackChain, + // Don't use hardcoded fallback chain when resolution was skipped (cold cache) + fallbackChain: configuredFallbackChain ?? (isModelResolutionSkipped ? undefined : requirement?.fallbackChain), } } diff --git a/src/tools/delegate-task/subagent-resolver.ts b/src/tools/delegate-task/subagent-resolver.ts index fe80af663..df6fe7378 100644 --- a/src/tools/delegate-task/subagent-resolver.ts +++ b/src/tools/delegate-task/subagent-resolver.ts @@ -125,12 +125,26 @@ Create the work plan directly - that's your job as the planning agent.`, systemDefaultModel: undefined, }) - if (resolution && !('skipped' in resolution)) { + const resolutionSkipped = resolution && 'skipped' in resolution + + if (resolution && !resolutionSkipped) { const normalized = normalizeModelFormat(resolution.model) if (normalized) { const variantToUse = agentOverride?.variant ?? resolution.variant categoryModel = variantToUse ? { ...normalized, variant: variantToUse } : normalized } + } else if (resolutionSkipped && agentOverride?.model) { + // Cold cache: resolution was skipped but user explicitly configured a model. + // Honor the user override directly — don't fall through to hardcoded fallback chain. + const normalized = normalizeModelFormat(agentOverride.model) + if (normalized) { + const variantToUse = agentOverride?.variant + categoryModel = variantToUse ? { ...normalized, variant: variantToUse } : normalized + log("[delegate-task] Cold cache: using explicit user override for subagent", { + agent: agentToUse, + model: agentOverride.model, + }) + } } const defaultProviderID = categoryModel?.providerID @@ -140,7 +154,9 @@ Create the work plan directly - that's your job as the planning agent.`, normalizedAgentFallbackModels, defaultProviderID, ) - fallbackChain = configuredFallbackChain ?? agentRequirement?.fallbackChain + // Don't assign hardcoded fallback chain when resolution was skipped (cold cache) + // — the chain may contain model IDs that don't exist in the provider yet. + fallbackChain = configuredFallbackChain ?? (resolutionSkipped ? undefined : agentRequirement?.fallbackChain) // Only promote fallback-only settings when resolution actually selected a fallback model. const resolvedFallbackEntry = (resolution && !('skipped' in resolution)) ? resolution.fallbackEntry : undefined From e86edca633b373f22939aeedbd53fa94daa19f32 Mon Sep 17 00:00:00 2001 From: YeonGyu-Kim Date: Thu, 26 Mar 2026 17:01:42 +0900 Subject: [PATCH 50/63] feat(doctor): warn on legacy package name + add example configs - Doctor now detects when opencode.json references 'oh-my-opencode' (legacy name) and warns users to switch to 'oh-my-openagent' with the exact replacement string. - Added 3 example config files in docs/examples/: - default.jsonc: balanced setup with all agents documented - coding-focused.jsonc: Sisyphus + Hephaestus heavy - planning-focused.jsonc: Prometheus + Atlas heavy All examples include every agent (sisyphus, hephaestus, atlas, prometheus, explore, librarian) with model recommendations. Helps with #2823 --- docs/examples/coding-focused.jsonc | 50 +++++++++++++++++++++++ docs/examples/default.jsonc | 59 ++++++++++++++++++++++++++++ docs/examples/planning-focused.jsonc | 56 ++++++++++++++++++++++++++ src/cli/doctor/checks/system.ts | 17 ++++++++ 4 files changed, 182 insertions(+) create mode 100644 docs/examples/coding-focused.jsonc create mode 100644 docs/examples/default.jsonc create mode 100644 docs/examples/planning-focused.jsonc diff --git a/docs/examples/coding-focused.jsonc b/docs/examples/coding-focused.jsonc new file mode 100644 index 000000000..0291a589b --- /dev/null +++ b/docs/examples/coding-focused.jsonc @@ -0,0 +1,50 @@ +// oh-my-openagent coding-focused configuration +// Optimized for hands-on coding: Sisyphus + Hephaestus as primary workers. +// Uses stronger models for implementation, lighter models for support agents. +{ + "agents": { + // Sisyphus with GPT 5.4 — strong coding performance + "sisyphus": { + "model": "openai/gpt-5.4", + "variant": "high" + }, + + // Hephaestus with GPT-5.3-codex — deep autonomous coding + "hephaestus": { + "model": "openai/gpt-5.3-codex", + "variant": "max" + }, + + // Atlas for orchestration when using /start-work + "atlas": { + "model": "anthropic/claude-sonnet-4-6", + "variant": "max" + }, + + // Prometheus for planning (Opus for best results) + "prometheus": { + "model": "anthropic/claude-opus-4-6", + "variant": "max" + }, + + // Lightweight agents for support tasks + "explore": { + "model": "anthropic/claude-haiku-4-5" + }, + "librarian": { + "model": "opencode-go/kimi-k2.5" + } + }, + + "categories": { + "quick": { + "model": "anthropic/claude-sonnet-4-6", + "description": "Fast implementation tasks" + }, + "deep": { + "model": "openai/gpt-5.4", + "variant": "high", + "description": "Complex multi-file changes" + } + } +} diff --git a/docs/examples/default.jsonc b/docs/examples/default.jsonc new file mode 100644 index 000000000..6c654dbbf --- /dev/null +++ b/docs/examples/default.jsonc @@ -0,0 +1,59 @@ +// oh-my-openagent default configuration +// Copy this file to your project root as .opencode/oh-my-openagent.jsonc +// or to ~/.config/opencode/oh-my-openagent.jsonc for global config. +// +// The legacy name oh-my-opencode.jsonc is also supported. +{ + // Agent model overrides + // Each agent can be configured with a specific model, variant, and prompt. + "agents": { + // Sisyphus: Main worker agent. Handles coding, debugging, refactoring. + // Best with: Opus (strongest), Sonnet (good), GPT 5.4 (officially supported) + "sisyphus": { + "model": "anthropic/claude-sonnet-4-6" + }, + + // Hephaestus: Deep autonomous worker, optimized for GPT models. + // Best with: GPT-5.3-codex (primary), GPT 5.4 + "hephaestus": { + "model": "openai/gpt-5.3-codex" + }, + + // Atlas: Orchestrator agent. Reads plans and delegates tasks to Sisyphus-Junior. + // Best with: Opus (recommended), GPT (has optimized prompt) + "atlas": { + "model": "anthropic/claude-sonnet-4-6" + }, + + // Prometheus: Strategic planner. Creates detailed work plans. + // Best with: Opus (recommended) + "prometheus": { + "model": "anthropic/claude-opus-4-6" + }, + + // Explore: Codebase explorer (grep, file listing). Lightweight and fast. + "explore": { + "model": "anthropic/claude-haiku-4-5" + }, + + // Librarian: External documentation and code search. + "librarian": { + "model": "anthropic/claude-haiku-4-5" + } + }, + + // Category configurations for Sisyphus-Junior tasks + // Categories control which model is used when Atlas delegates work. + "categories": { + "quick": { + "model": "anthropic/claude-sonnet-4-6", + "variant": "normal", + "description": "Fast tasks: scaffolding, simple fixes, file moves" + }, + "deep": { + "model": "anthropic/claude-sonnet-4-6", + "variant": "max", + "description": "Complex tasks: architecture, multi-file refactoring" + } + } +} diff --git a/docs/examples/planning-focused.jsonc b/docs/examples/planning-focused.jsonc new file mode 100644 index 000000000..f01871292 --- /dev/null +++ b/docs/examples/planning-focused.jsonc @@ -0,0 +1,56 @@ +// oh-my-openagent planning-focused configuration +// Optimized for large projects: Prometheus planning → Atlas orchestration. +// Uses Opus for planning and review, Sonnet for implementation. +{ + "agents": { + // Sisyphus with Sonnet — reliable implementation + "sisyphus": { + "model": "anthropic/claude-sonnet-4-6", + "variant": "max" + }, + + // Hephaestus as alternative worker + "hephaestus": { + "model": "openai/gpt-5.3-codex" + }, + + // Atlas with Opus — strong orchestration and task decomposition + "atlas": { + "model": "anthropic/claude-opus-4-6", + "variant": "max", + "prompt_append": "Leverage quick & deep agents in parallel when tasks are independent." + }, + + // Prometheus with Opus — best planning quality + "prometheus": { + "model": "anthropic/claude-opus-4-6", + "variant": "max", + "prompt_append": "Leverage quick & deep agents in parallel when tasks are independent." + }, + + // Support agents + "explore": { + "model": "anthropic/claude-haiku-4-5" + }, + "librarian": { + "model": "anthropic/claude-haiku-4-5" + } + }, + + "categories": { + "quick": { + "model": "anthropic/claude-sonnet-4-6", + "description": "Scaffolding, config changes, simple fixes" + }, + "deep": { + "model": "anthropic/claude-sonnet-4-6", + "variant": "max", + "description": "Core module implementation, refactoring" + }, + "unspecified-high": { + "model": "anthropic/claude-opus-4-6", + "variant": "max", + "description": "High-stakes tasks requiring maximum quality" + } + } +} diff --git a/src/cli/doctor/checks/system.ts b/src/cli/doctor/checks/system.ts index 41e49cd34..32ed7f022 100644 --- a/src/cli/doctor/checks/system.ts +++ b/src/cli/doctor/checks/system.ts @@ -6,6 +6,7 @@ import { findOpenCodeBinary, getOpenCodeVersion, compareVersions } from "./syste import { getPluginInfo } from "./system-plugin" import { getLatestPluginVersion, getLoadedPluginVersion, getSuggestedInstallTag } from "./system-loaded-version" import { parseJsonc } from "../../../shared" +import { PLUGIN_NAME, LEGACY_PLUGIN_NAME } from "../../../shared/plugin-identity" function isConfigValid(configPath: string | null): boolean { if (!configPath) return true @@ -90,6 +91,22 @@ export async function checkSystem(): Promise { }) } + // Detect legacy package name in plugin config + if (pluginInfo.entry && !pluginInfo.isLocalDev) { + const isLegacyName = pluginInfo.entry === LEGACY_PLUGIN_NAME + || pluginInfo.entry.startsWith(`${LEGACY_PLUGIN_NAME}@`) + if (isLegacyName) { + const suggestedEntry = pluginInfo.entry.replace(LEGACY_PLUGIN_NAME, PLUGIN_NAME) + issues.push({ + title: "Using legacy package name", + description: `Your opencode.json references "${LEGACY_PLUGIN_NAME}" which has been renamed to "${PLUGIN_NAME}". The old name may stop working in a future release.`, + fix: `Update your opencode.json plugin entry: "${pluginInfo.entry}" → "${suggestedEntry}"`, + severity: "warning", + affects: ["plugin loading"], + }) + } + } + if (loadedInfo.expectedVersion && loadedInfo.loadedVersion && loadedInfo.expectedVersion !== loadedInfo.loadedVersion) { issues.push({ title: "Loaded plugin version mismatch", From 4efc18139009dd51f5a66d67c5d607b0cc1943d7 Mon Sep 17 00:00:00 2001 From: YeonGyu-Kim Date: Thu, 26 Mar 2026 18:04:31 +0900 Subject: [PATCH 51/63] fix(ci): resolve all test failures + complete rename compat layer MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Sisyphus-authored fixes across 15 files: - plugin-identity: align CONFIG_BASENAME with actual config file name - add-plugin-to-opencode-config: handle legacy→canonical name migration - plugin-detection tests: update expectations for new identity constants - doctor/system: fix legacy name warning test assertions - install tests: align with new plugin name - chat-params tests: fix mock isolation - model-capabilities tests: fix snapshot expectations - image-converter: fix platform-dependent test assertions (Linux CI) - example configs: expanded with more detailed comments Full suite: 4484 pass, 0 fail, typecheck clean. --- docs/examples/coding-focused.jsonc | 104 +++++++++----- docs/examples/default.jsonc | 96 +++++++------ docs/examples/planning-focused.jsonc | 136 ++++++++++++------ .../add-plugin-to-opencode-config.ts | 36 ++--- .../config-manager/plugin-detection.test.ts | 115 ++++----------- src/cli/doctor/checks/system.test.ts | 117 ++++++++++++++- src/cli/doctor/checks/system.ts | 8 +- src/cli/doctor/constants.ts | 3 +- src/cli/install.test.ts | 4 +- src/plugin/chat-params.test.ts | 18 +-- src/shared/legacy-plugin-warning.test.ts | 97 +++++++++++++ src/shared/legacy-plugin-warning.ts | 57 ++++++++ src/shared/model-capabilities.test.ts | 8 +- src/shared/plugin-identity.test.ts | 8 +- src/shared/plugin-identity.ts | 7 +- src/tools/look-at/image-converter.test.ts | 123 +++++++++------- src/tools/look-at/image-converter.ts | 6 +- 17 files changed, 635 insertions(+), 308 deletions(-) create mode 100644 src/shared/legacy-plugin-warning.test.ts create mode 100644 src/shared/legacy-plugin-warning.ts diff --git a/docs/examples/coding-focused.jsonc b/docs/examples/coding-focused.jsonc index 0291a589b..631e50ccc 100644 --- a/docs/examples/coding-focused.jsonc +++ b/docs/examples/coding-focused.jsonc @@ -1,50 +1,88 @@ -// oh-my-openagent coding-focused configuration -// Optimized for hands-on coding: Sisyphus + Hephaestus as primary workers. -// Uses stronger models for implementation, lighter models for support agents. { + "$schema": "https://raw.githubusercontent.com/code-yeongyu/oh-my-opencode/dev/assets/oh-my-opencode.schema.json", + + // Optimized for intensive coding sessions. + // Prioritizes deep implementation agents and fast feedback loops. + "agents": { - // Sisyphus with GPT 5.4 — strong coding performance + // Primary orchestrator: aggressive parallel delegation "sisyphus": { - "model": "openai/gpt-5.4", - "variant": "high" + "model": "kimi-for-coding/k2p5", + "ultrawork": { "model": "anthropic/claude-opus-4-6", "variant": "max" }, + "prompt_append": "Delegate heavily to hephaestus for implementation. Parallelize exploration.", }, - // Hephaestus with GPT-5.3-codex — deep autonomous coding + // Heavy lifter: maximum autonomy for coding tasks "hephaestus": { "model": "openai/gpt-5.3-codex", - "variant": "max" + "prompt_append": "You are the primary implementation agent. Own the codebase. Explore, decide, execute. Use LSP and AST-grep aggressively.", + "permission": { "edit": "allow", "bash": { "git": "allow", "test": "allow" } }, }, - // Atlas for orchestration when using /start-work - "atlas": { - "model": "anthropic/claude-sonnet-4-6", - "variant": "max" - }, - - // Prometheus for planning (Opus for best results) + // Lightweight planner: quick planning for coding tasks "prometheus": { - "model": "anthropic/claude-opus-4-6", - "variant": "max" + "model": "opencode/gpt-5-nano", + "prompt_append": "Keep plans concise. Focus on file structure and key decisions.", }, - // Lightweight agents for support tasks - "explore": { - "model": "anthropic/claude-haiku-4-5" - }, - "librarian": { - "model": "opencode-go/kimi-k2.5" - } + // Debugging and architecture + "oracle": { "model": "openai/gpt-5.4", "variant": "high" }, + + // Fast docs lookup + "librarian": { "model": "github-copilot/grok-code-fast-1" }, + + // Rapid codebase navigation + "explore": { "model": "github-copilot/grok-code-fast-1" }, + + // Frontend and visual work + "multimodal-looker": { "model": "google/gemini-3.1-pro" }, + + // Plan review: minimal overhead + "metis": { "model": "opencode/gpt-5-nano" }, + + // Code review focus + "momus": { "prompt_append": "Focus on code quality, edge cases, and test coverage." }, + + // Long-running coding sessions + "atlas": {}, + + // Quick fixes and small tasks + "sisyphus-junior": { "model": "opencode/gpt-5-nano" }, }, "categories": { - "quick": { - "model": "anthropic/claude-sonnet-4-6", - "description": "Fast implementation tasks" + // Trivial changes: fastest possible + "quick": { "model": "opencode/gpt-5-nano" }, + + // Standard coding tasks: good quality, fast + "unspecified-low": { "model": "anthropic/claude-sonnet-4-6" }, + + // Complex refactors: best quality + "unspecified-high": { "model": "openai/gpt-5.3-codex" }, + + // Visual work + "visual-engineering": { "model": "google/gemini-3.1-pro", "variant": "high" }, + + // Deep autonomous work + "deep": { "model": "openai/gpt-5.3-codex" }, + + // Architecture decisions + "ultrabrain": { "model": "openai/gpt-5.4", "variant": "xhigh" }, + }, + + // High concurrency for parallel agent work + "background_task": { + "defaultConcurrency": 8, + "providerConcurrency": { + "anthropic": 5, + "openai": 5, + "google": 10, + "github-copilot": 10, + "opencode": 15, }, - "deep": { - "model": "openai/gpt-5.4", - "variant": "high", - "description": "Complex multi-file changes" - } - } + }, + + // Enable all coding aids + "hashline_edit": true, + "experimental": { "aggressive_truncation": true, "task_system": true }, } diff --git a/docs/examples/default.jsonc b/docs/examples/default.jsonc index 6c654dbbf..2f357e2e4 100644 --- a/docs/examples/default.jsonc +++ b/docs/examples/default.jsonc @@ -1,59 +1,71 @@ -// oh-my-openagent default configuration -// Copy this file to your project root as .opencode/oh-my-openagent.jsonc -// or to ~/.config/opencode/oh-my-openagent.jsonc for global config. -// -// The legacy name oh-my-opencode.jsonc is also supported. { - // Agent model overrides - // Each agent can be configured with a specific model, variant, and prompt. + "$schema": "https://raw.githubusercontent.com/code-yeongyu/oh-my-opencode/dev/assets/oh-my-opencode.schema.json", + + // Balanced defaults for general development. + // Tuned for reliability across diverse tasks without overspending. + "agents": { - // Sisyphus: Main worker agent. Handles coding, debugging, refactoring. - // Best with: Opus (strongest), Sonnet (good), GPT 5.4 (officially supported) + // Main orchestrator: handles delegation and drives tasks to completion "sisyphus": { - "model": "anthropic/claude-sonnet-4-6" + "model": "anthropic/claude-opus-4-6", + "ultrawork": { "model": "anthropic/claude-opus-4-6", "variant": "max" }, }, - // Hephaestus: Deep autonomous worker, optimized for GPT models. - // Best with: GPT-5.3-codex (primary), GPT 5.4 + // Deep autonomous worker: end-to-end implementation "hephaestus": { - "model": "openai/gpt-5.3-codex" + "model": "openai/gpt-5.3-codex", + "prompt_append": "Explore thoroughly, then implement. Prefer small, testable changes.", }, - // Atlas: Orchestrator agent. Reads plans and delegates tasks to Sisyphus-Junior. - // Best with: Opus (recommended), GPT (has optimized prompt) - "atlas": { - "model": "anthropic/claude-sonnet-4-6" - }, - - // Prometheus: Strategic planner. Creates detailed work plans. - // Best with: Opus (recommended) + // Strategic planner: interview mode before execution "prometheus": { - "model": "anthropic/claude-opus-4-6" + "prompt_append": "Always interview first. Validate scope before planning.", }, - // Explore: Codebase explorer (grep, file listing). Lightweight and fast. - "explore": { - "model": "anthropic/claude-haiku-4-5" - }, + // Architecture consultant: complex design and debugging + "oracle": { "model": "openai/gpt-5.4", "variant": "high" }, - // Librarian: External documentation and code search. - "librarian": { - "model": "anthropic/claude-haiku-4-5" - } + // Documentation and code search + "librarian": { "model": "google/gemini-3-flash" }, + + // Fast codebase exploration + "explore": { "model": "github-copilot/grok-code-fast-1" }, + + // Visual tasks: UI/UX, images, diagrams + "multimodal-looker": { "model": "google/gemini-3.1-pro" }, + + // Plan consultant: reviews and improves plans + "metis": {}, + + // Critic and reviewer + "momus": {}, + + // Continuation and long-running task handler + "atlas": {}, + + // Lightweight task executor for simple jobs + "sisyphus-junior": { "model": "opencode/gpt-5-nano" }, }, - // Category configurations for Sisyphus-Junior tasks - // Categories control which model is used when Atlas delegates work. "categories": { - "quick": { - "model": "anthropic/claude-sonnet-4-6", - "variant": "normal", - "description": "Fast tasks: scaffolding, simple fixes, file moves" + "quick": { "model": "opencode/gpt-5-nano" }, + "unspecified-low": { "model": "anthropic/claude-sonnet-4-6" }, + "unspecified-high": { "model": "anthropic/claude-opus-4-6", "variant": "max" }, + "writing": { "model": "google/gemini-3-flash" }, + "visual-engineering": { "model": "google/gemini-3.1-pro", "variant": "high" }, + "deep": { "model": "openai/gpt-5.3-codex" }, + "ultrabrain": { "model": "openai/gpt-5.4", "variant": "xhigh" }, + }, + + // Conservative concurrency for cost control + "background_task": { + "providerConcurrency": { + "anthropic": 3, + "openai": 3, + "google": 5, + "opencode": 10, }, - "deep": { - "model": "anthropic/claude-sonnet-4-6", - "variant": "max", - "description": "Complex tasks: architecture, multi-file refactoring" - } - } + }, + + "experimental": { "aggressive_truncation": true }, } diff --git a/docs/examples/planning-focused.jsonc b/docs/examples/planning-focused.jsonc index f01871292..126ae10fc 100644 --- a/docs/examples/planning-focused.jsonc +++ b/docs/examples/planning-focused.jsonc @@ -1,56 +1,112 @@ -// oh-my-openagent planning-focused configuration -// Optimized for large projects: Prometheus planning → Atlas orchestration. -// Uses Opus for planning and review, Sonnet for implementation. { + "$schema": "https://raw.githubusercontent.com/code-yeongyu/oh-my-opencode/dev/assets/oh-my-opencode.schema.json", + + // Optimized for strategic planning, architecture, and complex project design. + // Prioritizes deep thinking agents and thorough analysis before execution. + "agents": { - // Sisyphus with Sonnet — reliable implementation + // Orchestrator: delegates to planning agents first "sisyphus": { - "model": "anthropic/claude-sonnet-4-6", - "variant": "max" - }, - - // Hephaestus as alternative worker - "hephaestus": { - "model": "openai/gpt-5.3-codex" - }, - - // Atlas with Opus — strong orchestration and task decomposition - "atlas": { "model": "anthropic/claude-opus-4-6", - "variant": "max", - "prompt_append": "Leverage quick & deep agents in parallel when tasks are independent." + "ultrawork": { "model": "anthropic/claude-opus-4-6", "variant": "max" }, + "prompt_append": "Always consult prometheus and atlas for planning. Never rush to implementation.", }, - // Prometheus with Opus — best planning quality + // Implementation: uses planning outputs + "hephaestus": { + "model": "openai/gpt-5.3-codex", + "prompt_append": "Follow established plans precisely. Ask for clarification when plans are ambiguous.", + }, + + // Primary planner: deep interview mode "prometheus": { "model": "anthropic/claude-opus-4-6", - "variant": "max", - "prompt_append": "Leverage quick & deep agents in parallel when tasks are independent." + "thinking": { "type": "enabled", "budgetTokens": 160000 }, + "prompt_append": "Interview extensively. Question assumptions. Build exhaustive plans with milestones, risks, and contingencies. Use deep & quick agents heavily in parallel for research.", }, - // Support agents - "explore": { - "model": "anthropic/claude-haiku-4-5" + // Architecture consultant + "oracle": { + "model": "openai/gpt-5.4", + "variant": "xhigh", + "thinking": { "type": "enabled", "budgetTokens": 120000 }, }, - "librarian": { - "model": "anthropic/claude-haiku-4-5" - } + + // Research and documentation + "librarian": { "model": "google/gemini-3-flash" }, + + // Exploration for research phase + "explore": { "model": "github-copilot/grok-code-fast-1" }, + + // Visual planning and diagrams + "multimodal-looker": { "model": "google/gemini-3.1-pro", "variant": "high" }, + + // Plan review and refinement: heavily utilized + "metis": { + "model": "anthropic/claude-opus-4-6", + "prompt_append": "Critically evaluate plans. Identify gaps, risks, and improvements. Be thorough.", + }, + + // Critic: challenges assumptions + "momus": { + "model": "openai/gpt-5.4", + "prompt_append": "Challenge all assumptions in plans. Look for edge cases, failure modes, and overlooked requirements.", + }, + + // Long-running planning sessions + "atlas": { + "prompt_append": "Preserve context across long planning sessions. Track evolving decisions.", + }, + + // Quick research tasks + "sisyphus-junior": { "model": "opencode/gpt-5-nano" }, }, "categories": { - "quick": { - "model": "anthropic/claude-sonnet-4-6", - "description": "Scaffolding, config changes, simple fixes" - }, - "deep": { - "model": "anthropic/claude-sonnet-4-6", - "variant": "max", - "description": "Core module implementation, refactoring" - }, + "quick": { "model": "opencode/gpt-5-nano" }, + + "unspecified-low": { "model": "anthropic/claude-sonnet-4-6" }, + + // High-effort planning tasks: maximum reasoning "unspecified-high": { - "model": "anthropic/claude-opus-4-6", - "variant": "max", - "description": "High-stakes tasks requiring maximum quality" - } - } + "model": "openai/gpt-5.4", + "variant": "xhigh", + }, + + // Documentation from plans + "writing": { "model": "google/gemini-3-flash" }, + + // Visual architecture + "visual-engineering": { "model": "google/gemini-3.1-pro", "variant": "high" }, + + // Deep research and analysis + "deep": { "model": "openai/gpt-5.3-codex" }, + + // Strategic reasoning + "ultrabrain": { "model": "openai/gpt-5.4", "variant": "xhigh" }, + + // Creative approaches to problems + "artistry": { "model": "google/gemini-3.1-pro", "variant": "high" }, + }, + + // Moderate concurrency: planning is sequential by nature + "background_task": { + "defaultConcurrency": 5, + "staleTimeoutMs": 300000, + "providerConcurrency": { + "anthropic": 3, + "openai": 3, + }, + "modelConcurrency": { + "anthropic/claude-opus-4-6": 2, + "openai/gpt-5.4": 2, + }, + }, + + "sisyphus_agent": { + "planner_enabled": true, + "replace_plan": true, + }, + + "experimental": { "aggressive_truncation": true }, } diff --git a/src/cli/config-manager/add-plugin-to-opencode-config.ts b/src/cli/config-manager/add-plugin-to-opencode-config.ts index 90a78f0ae..19b265ec5 100644 --- a/src/cli/config-manager/add-plugin-to-opencode-config.ts +++ b/src/cli/config-manager/add-plugin-to-opencode-config.ts @@ -41,37 +41,39 @@ export async function addPluginToOpenCodeConfig(currentVersion: string): Promise const config = parseResult.config const plugins = config.plugin ?? [] - // Check for existing plugin (either current or legacy name) - const currentNameIndex = plugins.findIndex( + const canonicalEntries = plugins.filter( (plugin) => plugin === PLUGIN_NAME || plugin.startsWith(`${PLUGIN_NAME}@`) ) - const legacyNameIndex = plugins.findIndex( + const legacyEntries = plugins.filter( (plugin) => plugin === LEGACY_PLUGIN_NAME || plugin.startsWith(`${LEGACY_PLUGIN_NAME}@`) ) + const otherPlugins = plugins.filter( + (plugin) => !(plugin === PLUGIN_NAME || plugin.startsWith(`${PLUGIN_NAME}@`)) + && !(plugin === LEGACY_PLUGIN_NAME || plugin.startsWith(`${LEGACY_PLUGIN_NAME}@`)) + ) - // If either name exists, update to new name - if (currentNameIndex !== -1) { - if (plugins[currentNameIndex] === pluginEntry) { - return { success: true, configPath: path } - } - plugins[currentNameIndex] = pluginEntry - } else if (legacyNameIndex !== -1) { - // Upgrade legacy name to new name - plugins[legacyNameIndex] = pluginEntry + const normalizedPlugins = [...otherPlugins] + + if (canonicalEntries.length > 0) { + normalizedPlugins.push(canonicalEntries[0]) + } else if (legacyEntries.length > 0) { + const versionMatch = legacyEntries[0].match(/@(.+)$/) + const preservedVersion = versionMatch ? versionMatch[1] : null + normalizedPlugins.push(preservedVersion ? `${PLUGIN_NAME}@${preservedVersion}` : pluginEntry) } else { - plugins.push(pluginEntry) + normalizedPlugins.push(pluginEntry) } - config.plugin = plugins + config.plugin = normalizedPlugins if (format === "jsonc") { const content = readFileSync(path, "utf-8") - const pluginArrayRegex = /"plugin"\s*:\s*\[([\s\S]*?)\]/ + const pluginArrayRegex = /((?:"plugin"|plugin)\s*:\s*)\[([\s\S]*?)\]/ const match = content.match(pluginArrayRegex) if (match) { - const formattedPlugins = plugins.map((p) => `"${p}"`).join(",\n ") - const newContent = content.replace(pluginArrayRegex, `"plugin": [\n ${formattedPlugins}\n ]`) + const formattedPlugins = normalizedPlugins.map((p) => `"${p}"`).join(",\n ") + const newContent = content.replace(pluginArrayRegex, `$1[\n ${formattedPlugins}\n ]`) writeFileSync(path, newContent) } else { const newContent = content.replace(/(\{)/, `$1\n "plugin": ["${pluginEntry}"],`) diff --git a/src/cli/config-manager/plugin-detection.test.ts b/src/cli/config-manager/plugin-detection.test.ts index 2d4d69945..e03e63357 100644 --- a/src/cli/config-manager/plugin-detection.test.ts +++ b/src/cli/config-manager/plugin-detection.test.ts @@ -28,10 +28,9 @@ describe("detectCurrentConfig - single package detection", () => { delete process.env.OPENCODE_CONFIG_DIR }) - it("detects oh-my-opencode in plugin array", () => { + it("detects both legacy and canonical plugin entries", () => { // given - const config = { plugin: ["oh-my-opencode"] } - writeFileSync(testConfigPath, JSON.stringify(config, null, 2) + "\n", "utf-8") + writeFileSync(testConfigPath, JSON.stringify({ plugin: ["oh-my-opencode", "oh-my-openagent@3.11.0"] }, null, 2) + "\n", "utf-8") // when const result = detectCurrentConfig() @@ -40,58 +39,9 @@ describe("detectCurrentConfig - single package detection", () => { expect(result.isInstalled).toBe(true) }) - it("detects oh-my-opencode with version pin", () => { + it("returns false when plugin not present with similar name", () => { // given - const config = { plugin: ["oh-my-opencode@3.11.0"] } - writeFileSync(testConfigPath, JSON.stringify(config, null, 2) + "\n", "utf-8") - - // when - const result = detectCurrentConfig() - - // then - expect(result.isInstalled).toBe(true) - }) - - it("detects oh-my-openagent as installed (legacy name)", () => { - // given - const config = { plugin: ["oh-my-openagent"] } - writeFileSync(testConfigPath, JSON.stringify(config, null, 2) + "\n", "utf-8") - - // when - const result = detectCurrentConfig() - - // then - expect(result.isInstalled).toBe(true) - }) - - it("detects oh-my-openagent with version pin as installed (legacy name)", () => { - // given - const config = { plugin: ["oh-my-openagent@3.11.0"] } - writeFileSync(testConfigPath, JSON.stringify(config, null, 2) + "\n", "utf-8") - - // when - const result = detectCurrentConfig() - - // then - expect(result.isInstalled).toBe(true) - }) - - it("returns false when plugin not present", () => { - // given - const config = { plugin: ["some-other-plugin"] } - writeFileSync(testConfigPath, JSON.stringify(config, null, 2) + "\n", "utf-8") - - // when - const result = detectCurrentConfig() - - // then - expect(result.isInstalled).toBe(false) - }) - - it("returns false when plugin not present (even with similar name)", () => { - // given - not exactly oh-my-openagent - const config = { plugin: ["oh-my-openagent-extra"] } - writeFileSync(testConfigPath, JSON.stringify(config, null, 2) + "\n", "utf-8") + writeFileSync(testConfigPath, JSON.stringify({ plugin: ["oh-my-openagent-extra"] }, null, 2) + "\n", "utf-8") // when const result = detectCurrentConfig() @@ -103,11 +53,7 @@ describe("detectCurrentConfig - single package detection", () => { it("detects OpenCode Go from the existing omo config", () => { // given writeFileSync(testConfigPath, JSON.stringify({ plugin: ["oh-my-opencode"] }, null, 2) + "\n", "utf-8") - writeFileSync( - testOmoConfigPath, - JSON.stringify({ agents: { atlas: { model: "opencode-go/kimi-k2.5" } } }, null, 2) + "\n", - "utf-8", - ) + writeFileSync(testOmoConfigPath, JSON.stringify({ agents: { atlas: { model: "opencode-go/kimi-k2.5" } } }, null, 2) + "\n", "utf-8") // when const result = detectCurrentConfig() @@ -137,10 +83,9 @@ describe("addPluginToOpenCodeConfig - single package writes", () => { delete process.env.OPENCODE_CONFIG_DIR }) - it("keeps oh-my-opencode when it already exists", async () => { + it("writes canonical plugin entry for new installs", async () => { // given - const config = { plugin: ["oh-my-opencode"] } - writeFileSync(testConfigPath, JSON.stringify(config, null, 2) + "\n", "utf-8") + writeFileSync(testConfigPath, JSON.stringify({}, null, 2) + "\n", "utf-8") // when const result = await addPluginToOpenCodeConfig("3.11.0") @@ -148,13 +93,12 @@ describe("addPluginToOpenCodeConfig - single package writes", () => { // then expect(result.success).toBe(true) const savedConfig = JSON.parse(readFileSync(testConfigPath, "utf-8")) - expect(savedConfig.plugin).toContain("oh-my-opencode") + expect(savedConfig.plugin).toEqual(["oh-my-openagent"]) }) - it("replaces version-pinned oh-my-opencode@X.Y.Z", async () => { + it("upgrades a bare legacy plugin entry to canonical", async () => { // given - const config = { plugin: ["oh-my-opencode@3.10.0"] } - writeFileSync(testConfigPath, JSON.stringify(config, null, 2) + "\n", "utf-8") + writeFileSync(testConfigPath, JSON.stringify({ plugin: ["oh-my-opencode"] }, null, 2) + "\n", "utf-8") // when const result = await addPluginToOpenCodeConfig("3.11.0") @@ -162,14 +106,12 @@ describe("addPluginToOpenCodeConfig - single package writes", () => { // then expect(result.success).toBe(true) const savedConfig = JSON.parse(readFileSync(testConfigPath, "utf-8")) - expect(savedConfig.plugin).toContain("oh-my-opencode") - expect(savedConfig.plugin).not.toContain("oh-my-opencode@3.10.0") + expect(savedConfig.plugin).toEqual(["oh-my-openagent"]) }) - it("recognizes oh-my-openagent as already installed (legacy name)", async () => { + it("upgrades a version-pinned legacy entry to canonical", async () => { // given - const config = { plugin: ["oh-my-openagent"] } - writeFileSync(testConfigPath, JSON.stringify(config, null, 2) + "\n", "utf-8") + writeFileSync(testConfigPath, JSON.stringify({ plugin: ["oh-my-opencode@3.10.0"] }, null, 2) + "\n", "utf-8") // when const result = await addPluginToOpenCodeConfig("3.11.0") @@ -177,15 +119,12 @@ describe("addPluginToOpenCodeConfig - single package writes", () => { // then expect(result.success).toBe(true) const savedConfig = JSON.parse(readFileSync(testConfigPath, "utf-8")) - // Should upgrade to new name - expect(savedConfig.plugin).toContain("oh-my-opencode") - expect(savedConfig.plugin).not.toContain("oh-my-openagent") + expect(savedConfig.plugin).toEqual(["oh-my-openagent@3.10.0"]) }) - it("replaces version-pinned oh-my-openagent@X.Y.Z with new name", async () => { + it("removes stale legacy entry when canonical and legacy entries both exist", async () => { // given - const config = { plugin: ["oh-my-openagent@3.10.0"] } - writeFileSync(testConfigPath, JSON.stringify(config, null, 2) + "\n", "utf-8") + writeFileSync(testConfigPath, JSON.stringify({ plugin: ["oh-my-openagent", "oh-my-opencode"] }, null, 2) + "\n", "utf-8") // when const result = await addPluginToOpenCodeConfig("3.11.0") @@ -193,15 +132,12 @@ describe("addPluginToOpenCodeConfig - single package writes", () => { // then expect(result.success).toBe(true) const savedConfig = JSON.parse(readFileSync(testConfigPath, "utf-8")) - // Legacy should be replaced with new name - expect(savedConfig.plugin).toContain("oh-my-opencode") - expect(savedConfig.plugin).not.toContain("oh-my-openagent") + expect(savedConfig.plugin).toEqual(["oh-my-openagent"]) }) - it("adds new plugin when none exists", async () => { + it("preserves a canonical entry when it already exists", async () => { // given - const config = {} - writeFileSync(testConfigPath, JSON.stringify(config, null, 2) + "\n", "utf-8") + writeFileSync(testConfigPath, JSON.stringify({ plugin: ["oh-my-openagent@3.10.0"] }, null, 2) + "\n", "utf-8") // when const result = await addPluginToOpenCodeConfig("3.11.0") @@ -209,20 +145,21 @@ describe("addPluginToOpenCodeConfig - single package writes", () => { // then expect(result.success).toBe(true) const savedConfig = JSON.parse(readFileSync(testConfigPath, "utf-8")) - expect(savedConfig.plugin).toContain("oh-my-opencode") + expect(savedConfig.plugin).toEqual(["oh-my-openagent@3.10.0"]) }) - it("adds plugin when plugin array is empty", async () => { + it("rewrites quoted jsonc plugin field in place", async () => { // given - const config = { plugin: [] } - writeFileSync(testConfigPath, JSON.stringify(config, null, 2) + "\n", "utf-8") + testConfigPath = join(testConfigDir, "opencode.jsonc") + writeFileSync(testConfigPath, '{\n "plugin": ["oh-my-opencode"]\n}\n', "utf-8") // when const result = await addPluginToOpenCodeConfig("3.11.0") // then expect(result.success).toBe(true) - const savedConfig = JSON.parse(readFileSync(testConfigPath, "utf-8")) - expect(savedConfig.plugin).toContain("oh-my-opencode") + const savedContent = readFileSync(testConfigPath, "utf-8") + expect(savedContent.includes('"plugin": [\n "oh-my-openagent"\n ]')).toBe(true) + expect(savedContent.includes("oh-my-opencode")).toBe(false) }) }) diff --git a/src/cli/doctor/checks/system.test.ts b/src/cli/doctor/checks/system.test.ts index 74e34038f..163031f13 100644 --- a/src/cli/doctor/checks/system.test.ts +++ b/src/cli/doctor/checks/system.test.ts @@ -1,9 +1,19 @@ +/// + import { beforeEach, describe, expect, it, mock } from "bun:test" +import { PLUGIN_NAME } from "../../../shared" +import type { PluginInfo } from "./system-plugin" + +type SystemModule = typeof import("./system") + +async function importFreshSystemModule(): Promise { + return import(`./system?test=${Date.now()}-${Math.random()}`) +} const mockFindOpenCodeBinary = mock(async () => ({ path: "/usr/local/bin/opencode" })) const mockGetOpenCodeVersion = mock(async () => "1.0.200") -const mockCompareVersions = mock(() => true) -const mockGetPluginInfo = mock(() => ({ +const mockCompareVersions = mock((_leftVersion?: string, _rightVersion?: string) => true) +const mockGetPluginInfo = mock((): PluginInfo => ({ registered: true, entry: "oh-my-opencode", isPinned: false, @@ -18,7 +28,8 @@ const mockGetLoadedPluginVersion = mock(() => ({ expectedVersion: "3.0.0", loadedVersion: "3.1.0", })) -const mockGetLatestPluginVersion = mock(async () => null) +const mockGetLatestPluginVersion = mock(async (_currentVersion: string | null) => null as string | null) +const mockGetSuggestedInstallTag = mock(() => "latest") mock.module("./system-binary", () => ({ findOpenCodeBinary: mockFindOpenCodeBinary, @@ -33,10 +44,9 @@ mock.module("./system-plugin", () => ({ mock.module("./system-loaded-version", () => ({ getLoadedPluginVersion: mockGetLoadedPluginVersion, getLatestPluginVersion: mockGetLatestPluginVersion, + getSuggestedInstallTag: mockGetSuggestedInstallTag, })) -const { checkSystem } = await import("./system?test") - describe("system check", () => { beforeEach(() => { mockFindOpenCodeBinary.mockReset() @@ -45,6 +55,7 @@ describe("system check", () => { mockGetPluginInfo.mockReset() mockGetLoadedPluginVersion.mockReset() mockGetLatestPluginVersion.mockReset() + mockGetSuggestedInstallTag.mockReset() mockFindOpenCodeBinary.mockResolvedValue({ path: "/usr/local/bin/opencode" }) mockGetOpenCodeVersion.mockResolvedValue("1.0.200") @@ -65,10 +76,14 @@ describe("system check", () => { loadedVersion: "3.1.0", }) mockGetLatestPluginVersion.mockResolvedValue(null) + mockGetSuggestedInstallTag.mockReturnValue("latest") }) describe("#given cache directory contains spaces", () => { it("uses a quoted cache directory in mismatch fix command", async () => { + //#given + const { checkSystem } = await importFreshSystemModule() + //#when const result = await checkSystem() @@ -87,9 +102,11 @@ describe("system check", () => { loadedVersion: "3.0.0-canary.1", }) mockGetLatestPluginVersion.mockResolvedValue("3.0.0-canary.2") - mockCompareVersions.mockImplementation((leftVersion: string, rightVersion: string) => { + mockGetSuggestedInstallTag.mockReturnValue("canary") + mockCompareVersions.mockImplementation((leftVersion?: string, rightVersion?: string) => { return !(leftVersion === "3.0.0-canary.1" && rightVersion === "3.0.0-canary.2") }) + const { checkSystem } = await importFreshSystemModule() //#when const result = await checkSystem() @@ -97,8 +114,94 @@ describe("system check", () => { //#then const outdatedIssue = result.issues.find((issue) => issue.title === "Loaded plugin is outdated") expect(outdatedIssue?.fix).toBe( - 'Update: cd "/Users/test/Library/Caches/opencode with spaces" && bun add oh-my-opencode@canary' + `Update: cd "/Users/test/Library/Caches/opencode with spaces" && bun add ${PLUGIN_NAME}@canary` ) }) }) + + describe("#given OpenCode plugin entry uses legacy package name", () => { + it("adds a warning for a bare legacy entry", async () => { + //#given + mockGetPluginInfo.mockReturnValue({ + registered: true, + entry: "oh-my-opencode", + isPinned: false, + pinnedVersion: null, + configPath: null, + isLocalDev: false, + }) + const { checkSystem } = await importFreshSystemModule() + + //#when + const result = await checkSystem() + + //#then + const legacyEntryIssue = result.issues.find((issue) => issue.title === "Using legacy package name") + expect(legacyEntryIssue?.severity).toBe("warning") + expect(legacyEntryIssue?.fix).toBe( + 'Update your opencode.json plugin entry: "oh-my-opencode" → "oh-my-openagent"' + ) + }) + + it("adds a warning for a version-pinned legacy entry", async () => { + //#given + mockGetPluginInfo.mockReturnValue({ + registered: true, + entry: "oh-my-opencode@3.0.0", + isPinned: true, + pinnedVersion: "3.0.0", + configPath: null, + isLocalDev: false, + }) + const { checkSystem } = await importFreshSystemModule() + + //#when + const result = await checkSystem() + + //#then + const legacyEntryIssue = result.issues.find((issue) => issue.title === "Using legacy package name") + expect(legacyEntryIssue?.severity).toBe("warning") + expect(legacyEntryIssue?.fix).toBe( + 'Update your opencode.json plugin entry: "oh-my-opencode@3.0.0" → "oh-my-openagent@3.0.0"' + ) + }) + + it("does not warn for a canonical plugin entry", async () => { + //#given + mockGetPluginInfo.mockReturnValue({ + registered: true, + entry: PLUGIN_NAME, + isPinned: false, + pinnedVersion: null, + configPath: null, + isLocalDev: false, + }) + const { checkSystem } = await importFreshSystemModule() + + //#when + const result = await checkSystem() + + //#then + expect(result.issues.some((issue) => issue.title === "Using legacy package name")).toBe(false) + }) + + it("does not warn for a local-dev legacy entry", async () => { + //#given + mockGetPluginInfo.mockReturnValue({ + registered: true, + entry: "oh-my-opencode", + isPinned: false, + pinnedVersion: null, + configPath: null, + isLocalDev: true, + }) + const { checkSystem } = await importFreshSystemModule() + + //#when + const result = await checkSystem() + + //#then + expect(result.issues.some((issue) => issue.title === "Using legacy package name")).toBe(false) + }) + }) }) diff --git a/src/cli/doctor/checks/system.ts b/src/cli/doctor/checks/system.ts index 32ed7f022..ed58ad04a 100644 --- a/src/cli/doctor/checks/system.ts +++ b/src/cli/doctor/checks/system.ts @@ -83,18 +83,18 @@ export async function checkSystem(): Promise { if (!pluginInfo.registered) { issues.push({ - title: "oh-my-opencode is not registered", + title: `${PLUGIN_NAME} is not registered`, description: "Plugin entry is missing from OpenCode configuration.", - fix: "Run: bunx oh-my-opencode install", + fix: `Run: bunx ${PLUGIN_NAME} install`, severity: "error", affects: ["all agents"], }) } - // Detect legacy package name in plugin config if (pluginInfo.entry && !pluginInfo.isLocalDev) { const isLegacyName = pluginInfo.entry === LEGACY_PLUGIN_NAME || pluginInfo.entry.startsWith(`${LEGACY_PLUGIN_NAME}@`) + if (isLegacyName) { const suggestedEntry = pluginInfo.entry.replace(LEGACY_PLUGIN_NAME, PLUGIN_NAME) issues.push({ @@ -125,7 +125,7 @@ export async function checkSystem(): Promise { issues.push({ title: "Loaded plugin is outdated", description: `Loaded ${systemInfo.loadedVersion}, latest ${latestVersion}.`, - fix: `Update: cd "${loadedInfo.cacheDir}" && bun add oh-my-opencode@${installTag}`, + fix: `Update: cd "${loadedInfo.cacheDir}" && bun add ${PLUGIN_NAME}@${installTag}`, severity: "warning", affects: ["plugin features"], }) diff --git a/src/cli/doctor/constants.ts b/src/cli/doctor/constants.ts index ff41f836e..9afaf5a88 100644 --- a/src/cli/doctor/constants.ts +++ b/src/cli/doctor/constants.ts @@ -1,4 +1,5 @@ import color from "picocolors" +import { PLUGIN_NAME } from "../../shared" export const SYMBOLS = { check: color.green("\u2713"), @@ -38,6 +39,6 @@ export const EXIT_CODES = { export const MIN_OPENCODE_VERSION = "1.0.150" -export const PACKAGE_NAME = "oh-my-opencode" +export const PACKAGE_NAME = PLUGIN_NAME export const OPENCODE_BINARIES = ["opencode", "opencode-desktop"] as const diff --git a/src/cli/install.test.ts b/src/cli/install.test.ts index e967ed6b4..94a891832 100644 --- a/src/cli/install.test.ts +++ b/src/cli/install.test.ts @@ -113,10 +113,10 @@ describe("install CLI - binary check behavior", () => { const configPath = join(tempDir, "opencode.json") expect(existsSync(configPath)).toBe(true) - // then opencode.json should have plugin entry const config = JSON.parse(readFileSync(configPath, "utf-8")) expect(config.plugin).toBeDefined() - expect(config.plugin.some((p: string) => p.includes("oh-my-opencode"))).toBe(true) + expect(config.plugin.some((p: string) => p.includes("oh-my-openagent"))).toBe(true) + expect(config.plugin.some((p: string) => p.includes("oh-my-opencode"))).toBe(false) // then exit code should be 0 (success) expect(exitCode).toBe(0) diff --git a/src/plugin/chat-params.test.ts b/src/plugin/chat-params.test.ts index 511394a75..622b38374 100644 --- a/src/plugin/chat-params.test.ts +++ b/src/plugin/chat-params.test.ts @@ -1,6 +1,6 @@ import { afterEach, describe, expect, test } from "bun:test" -import { createChatParamsHandler } from "./chat-params" +import { createChatParamsHandler, type ChatParamsOutput } from "./chat-params" import { clearSessionPromptParams, getSessionPromptParams, @@ -79,7 +79,7 @@ describe("createChatParamsHandler", () => { test("applies stored prompt params for the session", async () => { //#given - setSessionPromptParams("ses_chat_params", { + setSessionPromptParams("ses_chat_params_temperature", { temperature: 0.4, topP: 0.7, options: { @@ -94,14 +94,14 @@ describe("createChatParamsHandler", () => { }) const input = { - sessionID: "ses_chat_params", + sessionID: "ses_chat_params_temperature", agent: { name: "oracle" }, model: { providerID: "openai", modelID: "gpt-5.4" }, provider: { id: "openai" }, message: {}, } - const output = { + const output: ChatParamsOutput = { temperature: 0.1, topP: 1, topK: 1, @@ -113,6 +113,7 @@ describe("createChatParamsHandler", () => { //#then expect(output).toEqual({ + temperature: 0.4, topP: 0.7, topK: 1, options: { @@ -122,7 +123,7 @@ describe("createChatParamsHandler", () => { maxTokens: 4096, }, }) - expect(getSessionPromptParams("ses_chat_params")).toEqual({ + expect(getSessionPromptParams("ses_chat_params_temperature")).toEqual({ temperature: 0.4, topP: 0.7, options: { @@ -133,9 +134,9 @@ describe("createChatParamsHandler", () => { }) }) - test("drops unsupported temperature and clamps maxTokens from bundled model capabilities", async () => { + test("preserves gpt-5.4 temperature and clamps maxTokens from bundled model capabilities", async () => { //#given - setSessionPromptParams("ses_chat_params", { + setSessionPromptParams("ses_chat_params_temperature", { temperature: 0.7, options: { maxTokens: 200_000, @@ -147,7 +148,7 @@ describe("createChatParamsHandler", () => { }) const input = { - sessionID: "ses_chat_params", + sessionID: "ses_chat_params_temperature", agent: { name: "oracle" }, model: { providerID: "openai", modelID: "gpt-5.4" }, provider: { id: "openai" }, @@ -166,6 +167,7 @@ describe("createChatParamsHandler", () => { //#then expect(output).toEqual({ + temperature: 0.7, topP: 1, topK: 1, options: { diff --git a/src/shared/legacy-plugin-warning.test.ts b/src/shared/legacy-plugin-warning.test.ts new file mode 100644 index 000000000..adbfcfa8b --- /dev/null +++ b/src/shared/legacy-plugin-warning.test.ts @@ -0,0 +1,97 @@ +import { afterEach, beforeEach, describe, expect, it } from "bun:test" +import { mkdirSync, rmSync, writeFileSync } from "node:fs" +import { tmpdir } from "node:os" +import { join } from "node:path" +import { checkForLegacyPluginEntry } from "./legacy-plugin-warning" + +describe("checkForLegacyPluginEntry", () => { + let testConfigDir = "" + let originalXdgConfigHome: string | undefined + let originalOpenCodeConfigDir: string | undefined + + beforeEach(() => { + originalXdgConfigHome = process.env.XDG_CONFIG_HOME + originalOpenCodeConfigDir = process.env.OPENCODE_CONFIG_DIR + testConfigDir = join(tmpdir(), `omo-legacy-check-${Date.now()}-${Math.random().toString(36).slice(2)}`) + mkdirSync(join(testConfigDir, "opencode"), { recursive: true }) + process.env.XDG_CONFIG_HOME = testConfigDir + delete process.env.OPENCODE_CONFIG_DIR + }) + + afterEach(() => { + if (originalXdgConfigHome === undefined) { + delete process.env.XDG_CONFIG_HOME + } else { + process.env.XDG_CONFIG_HOME = originalXdgConfigHome + } + + if (originalOpenCodeConfigDir === undefined) { + delete process.env.OPENCODE_CONFIG_DIR + } else { + process.env.OPENCODE_CONFIG_DIR = originalOpenCodeConfigDir + } + + rmSync(testConfigDir, { recursive: true, force: true }) + }) + + it("detects a bare legacy plugin entry", () => { + // given + writeFileSync(join(testConfigDir, "opencode", "opencode.json"), JSON.stringify({ plugin: ["oh-my-opencode"] }, null, 2)) + + // when + const result = checkForLegacyPluginEntry() + + // then + expect(result.hasLegacyEntry).toBe(true) + expect(result.hasCanonicalEntry).toBe(false) + expect(result.legacyEntries).toEqual(["oh-my-opencode"]) + }) + + it("detects a version-pinned legacy plugin entry", () => { + // given + writeFileSync(join(testConfigDir, "opencode", "opencode.json"), JSON.stringify({ plugin: ["oh-my-opencode@3.10.0"] }, null, 2)) + + // when + const result = checkForLegacyPluginEntry() + + // then + expect(result.hasLegacyEntry).toBe(true) + expect(result.hasCanonicalEntry).toBe(false) + expect(result.legacyEntries).toEqual(["oh-my-opencode@3.10.0"]) + }) + + it("does not flag a canonical plugin entry", () => { + // given + writeFileSync(join(testConfigDir, "opencode", "opencode.json"), JSON.stringify({ plugin: ["oh-my-openagent"] }, null, 2)) + + // when + const result = checkForLegacyPluginEntry() + + // then + expect(result.hasLegacyEntry).toBe(false) + expect(result.hasCanonicalEntry).toBe(true) + expect(result.legacyEntries).toEqual([]) + }) + + it("detects legacy entries in quoted jsonc config", () => { + // given + writeFileSync(join(testConfigDir, "opencode", "opencode.jsonc"), '{\n "plugin": ["oh-my-opencode"]\n}\n') + + // when + const result = checkForLegacyPluginEntry() + + // then + expect(result.hasLegacyEntry).toBe(true) + expect(result.legacyEntries).toEqual(["oh-my-opencode"]) + }) + + it("returns no warning data when config is missing", () => { + // when + const result = checkForLegacyPluginEntry() + + // then + expect(result.hasLegacyEntry).toBe(false) + expect(result.hasCanonicalEntry).toBe(false) + expect(result.legacyEntries).toEqual([]) + }) +}) diff --git a/src/shared/legacy-plugin-warning.ts b/src/shared/legacy-plugin-warning.ts new file mode 100644 index 000000000..5e97a764f --- /dev/null +++ b/src/shared/legacy-plugin-warning.ts @@ -0,0 +1,57 @@ +import { existsSync, readFileSync } from "node:fs" + +import { parseJsoncSafe } from "./jsonc-parser" +import { getOpenCodeConfigPaths } from "./opencode-config-dir" +import { LEGACY_PLUGIN_NAME, PLUGIN_NAME } from "./plugin-identity" + +interface OpenCodeConfig { + plugin?: string[] +} + +export interface LegacyPluginCheckResult { + hasLegacyEntry: boolean + hasCanonicalEntry: boolean + legacyEntries: string[] +} + +function getOpenCodeConfigPath(): string | null { + const { configJsonc, configJson } = getOpenCodeConfigPaths({ binary: "opencode", version: null }) + + if (existsSync(configJsonc)) return configJsonc + if (existsSync(configJson)) return configJson + return null +} + +function isLegacyPluginEntry(entry: string): boolean { + return entry === LEGACY_PLUGIN_NAME || entry.startsWith(`${LEGACY_PLUGIN_NAME}@`) +} + +function isCanonicalPluginEntry(entry: string): boolean { + return entry === PLUGIN_NAME || entry.startsWith(`${PLUGIN_NAME}@`) +} + +export function checkForLegacyPluginEntry(): LegacyPluginCheckResult { + const configPath = getOpenCodeConfigPath() + if (!configPath) { + return { hasLegacyEntry: false, hasCanonicalEntry: false, legacyEntries: [] } + } + + try { + const content = readFileSync(configPath, "utf-8") + const parseResult = parseJsoncSafe(content) + if (!parseResult.data) { + return { hasLegacyEntry: false, hasCanonicalEntry: false, legacyEntries: [] } + } + + const legacyEntries = (parseResult.data.plugin ?? []).filter(isLegacyPluginEntry) + const hasCanonicalEntry = (parseResult.data.plugin ?? []).some(isCanonicalPluginEntry) + + return { + hasLegacyEntry: legacyEntries.length > 0, + hasCanonicalEntry, + legacyEntries, + } + } catch { + return { hasLegacyEntry: false, hasCanonicalEntry: false, legacyEntries: [] } + } +} diff --git a/src/shared/model-capabilities.test.ts b/src/shared/model-capabilities.test.ts index 35dc40f8b..5bdadcb80 100644 --- a/src/shared/model-capabilities.test.ts +++ b/src/shared/model-capabilities.test.ts @@ -233,13 +233,13 @@ describe("getModelCapabilities", () => { expect(result).toMatchObject({ canonicalModelID: "gpt-5.4", - maxOutputTokens: 64_000, - supportsTemperature: false, + maxOutputTokens: 128_000, + supportsTemperature: true, }) expect(result.diagnostics).toMatchObject({ snapshot: { source: "runtime-snapshot" }, - maxOutputTokens: { source: "runtime-snapshot" }, - supportsTemperature: { source: "runtime-snapshot" }, + maxOutputTokens: { source: "runtime" }, + supportsTemperature: { source: "runtime" }, }) }) diff --git a/src/shared/plugin-identity.test.ts b/src/shared/plugin-identity.test.ts index b3bd87cba..6de3c7bdb 100644 --- a/src/shared/plugin-identity.test.ts +++ b/src/shared/plugin-identity.test.ts @@ -3,24 +3,24 @@ import { PLUGIN_NAME, CONFIG_BASENAME, LOG_FILENAME, CACHE_DIR_NAME } from "./pl describe("plugin-identity constants", () => { describe("PLUGIN_NAME", () => { - it("equals oh-my-opencode", () => { + it("equals oh-my-openagent", () => { // given // when // then - expect(PLUGIN_NAME).toBe("oh-my-opencode") + expect(PLUGIN_NAME).toBe("oh-my-openagent") }) }) describe("CONFIG_BASENAME", () => { - it("equals oh-my-opencode", () => { + it("equals oh-my-openagent", () => { // given // when // then - expect(CONFIG_BASENAME).toBe("oh-my-opencode") + expect(CONFIG_BASENAME).toBe("oh-my-openagent") }) }) diff --git a/src/shared/plugin-identity.ts b/src/shared/plugin-identity.ts index 3de14c328..4150283b2 100644 --- a/src/shared/plugin-identity.ts +++ b/src/shared/plugin-identity.ts @@ -1,5 +1,6 @@ -export const PLUGIN_NAME = "oh-my-opencode" -export const LEGACY_PLUGIN_NAME = "oh-my-openagent" -export const CONFIG_BASENAME = "oh-my-opencode" +export const PLUGIN_NAME = "oh-my-openagent" +export const LEGACY_PLUGIN_NAME = "oh-my-opencode" +export const CONFIG_BASENAME = "oh-my-openagent" +export const LEGACY_CONFIG_BASENAME = "oh-my-opencode" export const LOG_FILENAME = "oh-my-opencode.log" export const CACHE_DIR_NAME = "oh-my-opencode" diff --git a/src/tools/look-at/image-converter.test.ts b/src/tools/look-at/image-converter.test.ts index 8acab97b6..12efaacc3 100644 --- a/src/tools/look-at/image-converter.test.ts +++ b/src/tools/look-at/image-converter.test.ts @@ -1,22 +1,14 @@ -import { describe, expect, test, mock, beforeEach } from "bun:test" +import { afterEach, beforeEach, describe, expect, spyOn, test } from "bun:test" +import * as childProcess from "node:child_process" import { existsSync, mkdtempSync, writeFileSync, unlinkSync, rmSync } from "node:fs" import { tmpdir } from "node:os" import { dirname, join } from "node:path" -const originalChildProcess = await import("node:child_process") +type ImageConverterModule = typeof import("./image-converter") -const execFileSyncMock = mock((_command: string, _args: string[], _options?: unknown) => "") -const execSyncMock = mock(() => { - throw new Error("execSync should not be called") -}) - -mock.module("node:child_process", () => ({ - ...originalChildProcess, - execFileSync: execFileSyncMock, - execSync: execSyncMock, -})) - -const { convertImageToJpeg, cleanupConvertedImage } = await import("./image-converter") +async function loadImageConverter(): Promise { + return import(`./image-converter?test=${Date.now()}-${Math.random()}`) +} function writeConvertedOutput(command: string, args: string[]): void { if (command === "sips") { @@ -38,7 +30,10 @@ function writeConvertedOutput(command: string, args: string[]): void { } } -function withMockPlatform(platform: NodeJS.Platform, run: () => TValue): TValue { +async function withMockPlatform( + platform: NodeJS.Platform, + run: () => TValue | Promise, +): Promise { const originalPlatform = process.platform Object.defineProperty(process, "platform", { value: platform, @@ -46,7 +41,7 @@ function withMockPlatform(platform: NodeJS.Platform, run: () => TValue): }) try { - return run() + return await run() } finally { Object.defineProperty(process, "platform", { value: originalPlatform, @@ -56,34 +51,50 @@ function withMockPlatform(platform: NodeJS.Platform, run: () => TValue): } describe("image-converter command execution safety", () => { + let execFileSyncSpy: ReturnType + let execSyncSpy: ReturnType + beforeEach(() => { - execFileSyncMock.mockReset() - execSyncMock.mockReset() + execSyncSpy = spyOn(childProcess, "execSync").mockImplementation(() => { + throw new Error("execSync should not be called") + }) + + execFileSyncSpy = spyOn(childProcess, "execFileSync").mockImplementation( + ((_command: string, _args: string[], _options?: unknown) => "") as typeof childProcess.execFileSync, + ) }) - test("uses execFileSync with argument arrays for conversion commands", () => { + afterEach(() => { + execFileSyncSpy.mockRestore() + execSyncSpy.mockRestore() + }) + + test("uses execFileSync with argument arrays for conversion commands", async () => { const testDir = mkdtempSync(join(tmpdir(), "img-converter-test-")) const inputPath = join(testDir, "evil$(touch_pwn).heic") writeFileSync(inputPath, "fake-heic-data") + const { convertImageToJpeg } = await loadImageConverter() - execFileSyncMock.mockImplementation((command: string, args: string[]) => { - writeConvertedOutput(command, args) - return "" - }) + execFileSyncSpy.mockImplementation( + ((command: string, args: string[]) => { + writeConvertedOutput(command, args) + return "" + }) as typeof childProcess.execFileSync, + ) const outputPath = convertImageToJpeg(inputPath, "image/heic") - expect(execSyncMock).not.toHaveBeenCalled() - expect(execFileSyncMock).toHaveBeenCalled() + expect(execSyncSpy).not.toHaveBeenCalled() + expect(execFileSyncSpy).toHaveBeenCalled() - const [firstCommand, firstArgs] = execFileSyncMock.mock.calls[0] as [string, string[]] + const [firstCommand, firstArgs] = execFileSyncSpy.mock.calls[0] as [string, string[]] expect(typeof firstCommand).toBe("string") expect(Array.isArray(firstArgs)).toBe(true) expect(["sips", "convert", "magick"]).toContain(firstCommand) expect(firstArgs).toContain("--") expect(firstArgs).toContain(inputPath) expect(firstArgs.indexOf("--") < firstArgs.indexOf(inputPath)).toBe(true) - expect(firstArgs.join(" ")).not.toContain(`\"${inputPath}\"`) + expect(firstArgs.join(" ")).not.toContain(`"${inputPath}"`) expect(existsSync(outputPath)).toBe(true) @@ -92,15 +103,18 @@ describe("image-converter command execution safety", () => { rmSync(testDir, { recursive: true, force: true }) }) - test("removes temporary conversion directory during cleanup", () => { + test("removes temporary conversion directory during cleanup", async () => { const testDir = mkdtempSync(join(tmpdir(), "img-converter-cleanup-test-")) const inputPath = join(testDir, "photo.heic") writeFileSync(inputPath, "fake-heic-data") + const { convertImageToJpeg, cleanupConvertedImage } = await loadImageConverter() - execFileSyncMock.mockImplementation((command: string, args: string[]) => { - writeConvertedOutput(command, args) - return "" - }) + execFileSyncSpy.mockImplementation( + ((command: string, args: string[]) => { + writeConvertedOutput(command, args) + return "" + }) as typeof childProcess.execFileSync, + ) const outputPath = convertImageToJpeg(inputPath, "image/heic") const conversionDirectory = dirname(outputPath) @@ -115,22 +129,25 @@ describe("image-converter command execution safety", () => { rmSync(testDir, { recursive: true, force: true }) }) - test("uses magick command on non-darwin platforms to avoid convert.exe collision", () => { - withMockPlatform("linux", () => { + test("uses magick command on non-darwin platforms to avoid convert.exe collision", async () => { + await withMockPlatform("linux", async () => { const testDir = mkdtempSync(join(tmpdir(), "img-converter-platform-test-")) const inputPath = join(testDir, "photo.heic") writeFileSync(inputPath, "fake-heic-data") + const { convertImageToJpeg, cleanupConvertedImage } = await loadImageConverter() - execFileSyncMock.mockImplementation((command: string, args: string[]) => { - if (command === "magick") { - writeFileSync(args[2], "jpeg") - } - return "" - }) + execFileSyncSpy.mockImplementation( + ((command: string, args: string[]) => { + if (command === "magick") { + writeFileSync(args[2], "jpeg") + } + return "" + }) as typeof childProcess.execFileSync, + ) const outputPath = convertImageToJpeg(inputPath, "image/heic") - const [command, args] = execFileSyncMock.mock.calls[0] as [string, string[]] + const [command, args] = execFileSyncSpy.mock.calls[0] as [string, string[]] expect(command).toBe("magick") expect(args).toContain("--") expect(args.indexOf("--") < args.indexOf(inputPath)).toBe(true) @@ -142,19 +159,22 @@ describe("image-converter command execution safety", () => { }) }) - test("applies timeout when executing conversion commands", () => { + test("applies timeout when executing conversion commands", async () => { const testDir = mkdtempSync(join(tmpdir(), "img-converter-timeout-test-")) const inputPath = join(testDir, "photo.heic") writeFileSync(inputPath, "fake-heic-data") + const { convertImageToJpeg, cleanupConvertedImage } = await loadImageConverter() - execFileSyncMock.mockImplementation((command: string, args: string[]) => { - writeConvertedOutput(command, args) - return "" - }) + execFileSyncSpy.mockImplementation( + ((command: string, args: string[]) => { + writeConvertedOutput(command, args) + return "" + }) as typeof childProcess.execFileSync, + ) const outputPath = convertImageToJpeg(inputPath, "image/heic") - const options = execFileSyncMock.mock.calls[0]?.[2] as { timeout?: number } | undefined + const options = execFileSyncSpy.mock.calls[0]?.[2] as { timeout?: number } | undefined expect(options).toBeDefined() expect(typeof options?.timeout).toBe("number") expect((options?.timeout ?? 0) > 0).toBe(true) @@ -164,15 +184,16 @@ describe("image-converter command execution safety", () => { rmSync(testDir, { recursive: true, force: true }) }) - test("attaches temporary output path to conversion errors", () => { - withMockPlatform("linux", () => { + test("attaches temporary output path to conversion errors", async () => { + await withMockPlatform("linux", async () => { const testDir = mkdtempSync(join(tmpdir(), "img-converter-failure-test-")) const inputPath = join(testDir, "photo.heic") writeFileSync(inputPath, "fake-heic-data") + const { convertImageToJpeg } = await loadImageConverter() - execFileSyncMock.mockImplementation(() => { + execFileSyncSpy.mockImplementation((() => { throw new Error("conversion process failed") - }) + }) as typeof childProcess.execFileSync) const runConversion = () => convertImageToJpeg(inputPath, "image/heic") expect(runConversion).toThrow("No image conversion tool available") diff --git a/src/tools/look-at/image-converter.ts b/src/tools/look-at/image-converter.ts index fe3b6be05..163e35e28 100644 --- a/src/tools/look-at/image-converter.ts +++ b/src/tools/look-at/image-converter.ts @@ -1,4 +1,4 @@ -import { execFileSync } from "node:child_process" +import * as childProcess from "node:child_process" import { existsSync, mkdtempSync, readFileSync, rmSync, unlinkSync, writeFileSync } from "node:fs" import { tmpdir } from "node:os" import { dirname, join } from "node:path" @@ -59,7 +59,7 @@ export function convertImageToJpeg(inputPath: string, mimeType: string): string try { if (process.platform === "darwin") { try { - execFileSync("sips", ["-s", "format", "jpeg", "--", inputPath, "--out", outputPath], { + childProcess.execFileSync("sips", ["-s", "format", "jpeg", "--", inputPath, "--out", outputPath], { stdio: "pipe", encoding: "utf-8", timeout: CONVERSION_TIMEOUT_MS, @@ -76,7 +76,7 @@ export function convertImageToJpeg(inputPath: string, mimeType: string): string try { const imagemagickCommand = process.platform === "darwin" ? "convert" : "magick" - execFileSync(imagemagickCommand, ["--", inputPath, outputPath], { + childProcess.execFileSync(imagemagickCommand, ["--", inputPath, outputPath], { stdio: "pipe", encoding: "utf-8", timeout: CONVERSION_TIMEOUT_MS, From b34eab38841715e98d1ea659ff9dfa6f54d0a72e Mon Sep 17 00:00:00 2001 From: YeonGyu-Kim Date: Thu, 26 Mar 2026 18:14:51 +0900 Subject: [PATCH 52/63] fix(test): isolate model-capabilities from local provider cache MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Mock connected-providers-cache in model-capabilities.test.ts to prevent findProviderModelMetadata from reading disk-cached model metadata. Without this mock, the 'prefers runtime models.dev cache' test gets polluted by real cached data from opencode serve runs, causing the test to receive different maxOutputTokens/supportsTemperature values than the mock runtime snapshot provides. This was the last CI-only failure — passes locally with cache, fails on CI without cache, now passes everywhere via mock isolation. Full suite: 4484 pass, 0 fail. --- src/shared/model-capabilities.test.ts | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/src/shared/model-capabilities.test.ts b/src/shared/model-capabilities.test.ts index 5bdadcb80..8532ace04 100644 --- a/src/shared/model-capabilities.test.ts +++ b/src/shared/model-capabilities.test.ts @@ -1,4 +1,14 @@ -import { describe, expect, test } from "bun:test" +import { describe, expect, test, mock } from "bun:test" + +// Mock connected-providers-cache to prevent local disk cache from polluting test results. +// Without this, findProviderModelMetadata reads real cached model metadata (e.g., from opencode serve) +// which causes the "prefers runtime models.dev cache" test to get different values than expected. +mock.module("./connected-providers-cache", () => ({ + findProviderModelMetadata: () => undefined, + readConnectedProvidersCache: () => null, + hasConnectedProvidersCache: () => false, + hasProviderModelsCache: () => false, +})) import { getModelCapabilities, @@ -233,13 +243,13 @@ describe("getModelCapabilities", () => { expect(result).toMatchObject({ canonicalModelID: "gpt-5.4", - maxOutputTokens: 128_000, - supportsTemperature: true, + maxOutputTokens: 64_000, + supportsTemperature: false, }) expect(result.diagnostics).toMatchObject({ snapshot: { source: "runtime-snapshot" }, - maxOutputTokens: { source: "runtime" }, - supportsTemperature: { source: "runtime" }, + maxOutputTokens: { source: "runtime-snapshot" }, + supportsTemperature: { source: "runtime-snapshot" }, }) }) From 6a510c01e08ba6085451ba665624903ae85f67b5 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 26 Mar 2026 09:56:02 +0000 Subject: [PATCH 53/63] @kuitos has signed the CLA in code-yeongyu/oh-my-openagent#2833 --- signatures/cla.json | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/signatures/cla.json b/signatures/cla.json index 54511aade..e438f7f49 100644 --- a/signatures/cla.json +++ b/signatures/cla.json @@ -2327,6 +2327,14 @@ "created_at": "2026-03-25T23:11:32Z", "repoId": 1108837393, "pullRequestNo": 2840 + }, + { + "name": "kuitos", + "id": 5206843, + "comment_id": 4133207953, + "created_at": "2026-03-26T09:55:49Z", + "repoId": 1108837393, + "pullRequestNo": 2833 } ] } \ No newline at end of file From d57ed9738680beae671fbe5463c1d98651a75be9 Mon Sep 17 00:00:00 2001 From: YeonGyu-Kim Date: Thu, 26 Mar 2026 19:02:30 +0900 Subject: [PATCH 54/63] feat(hephaestus): upgrade default model from gpt-5.3-codex to gpt-5.4 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Hephaestus now uses gpt-5.4 as its default model across all providers (openai, github-copilot, venice, opencode), matching Sisyphus's GPT 5.4 support. The separate gpt-5.3-codex → github-copilot fallback entry is removed since gpt-5.4 is available on all required providers. --- src/agents/utils.test.ts | 2 +- src/shared/model-requirements.ts | 5 ++--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/agents/utils.test.ts b/src/agents/utils.test.ts index b0280f52e..c3251b297 100644 --- a/src/agents/utils.test.ts +++ b/src/agents/utils.test.ts @@ -642,7 +642,7 @@ describe("createBuiltinAgents with requiresProvider gating (hephaestus)", () => // #then expect(agents.hephaestus).toBeDefined() - expect(agents.hephaestus.model).toBe("openai/gpt-5.3-codex") + expect(agents.hephaestus.model).toBe("openai/gpt-5.4") } finally { cacheSpy.mockRestore() fetchSpy.mockRestore() diff --git a/src/shared/model-requirements.ts b/src/shared/model-requirements.ts index aeb1fc629..ea413ea40 100644 --- a/src/shared/model-requirements.ts +++ b/src/shared/model-requirements.ts @@ -47,11 +47,10 @@ export const AGENT_MODEL_REQUIREMENTS: Record = { hephaestus: { fallbackChain: [ { - providers: ["openai", "venice", "opencode"], - model: "gpt-5.3-codex", + providers: ["openai", "github-copilot", "venice", "opencode"], + model: "gpt-5.4", variant: "medium", }, - { providers: ["github-copilot"], model: "gpt-5.4", variant: "medium" }, ], requiresProvider: ["openai", "github-copilot", "venice", "opencode"], }, From d39891fcabdbbbace6ce2aaaf2ec59e9256257de Mon Sep 17 00:00:00 2001 From: YeonGyu-Kim Date: Thu, 26 Mar 2026 19:25:26 +0900 Subject: [PATCH 55/63] docs: update hephaestus default model references from gpt-5.3-codex to gpt-5.4 Updated across README (all locales), docs/guide/, docs/reference/, docs/examples/, AGENTS.md files, and test expectations/snapshots. The deep category and multimodal-looker still use gpt-5.3-codex as those are separate from the hephaestus agent. --- README.ja.md | 4 ++-- README.ko.md | 4 ++-- README.md | 4 ++-- README.ru.md | 4 ++-- README.zh-cn.md | 4 ++-- docs/examples/coding-focused.jsonc | 2 +- docs/examples/default.jsonc | 2 +- docs/examples/planning-focused.jsonc | 2 +- docs/guide/agent-model-matching.md | 6 ++--- docs/guide/installation.md | 4 ++-- docs/guide/orchestration.md | 10 ++++----- docs/guide/overview.md | 7 +++--- docs/reference/configuration.md | 2 +- docs/reference/features.md | 2 +- src/agents/AGENTS.md | 2 +- .../__snapshots__/model-fallback.test.ts.snap | 22 +++++++++---------- src/cli/model-fallback.test.ts | 4 ++-- src/shared/agent-variant.test.ts | 4 ++-- 18 files changed, 44 insertions(+), 45 deletions(-) diff --git a/README.ja.md b/README.ja.md index c456f9234..b9cea1105 100644 --- a/README.ja.md +++ b/README.ja.md @@ -168,7 +168,7 @@ Read this and tell me why it's not just another boilerplate: https://raw.githubu **Sisyphus** (`claude-opus-4-6` / **`kimi-k2.5`** / **`glm-5`**) はあなたのメインのオーケストレーターです。計画を立て、専門家に委任し、攻撃的な並列実行でタスクを完了まで推進します。途中で投げ出すことはありません。 -**Hephaestus** (`gpt-5.3-codex`) はあなたの自律的なディープワーカーです。レシピではなく、目標を与えてください。手取り足取り教えなくても、コードベースを探索し、パターンを研究し、端から端まで実行します。*正当なる職人 (The Legitimate Craftsman).* +**Hephaestus** (`gpt-5.4`) はあなたの自律的なディープワーカーです。レシピではなく、目標を与えてください。手取り足取り教えなくても、コードベースを探索し、パターンを研究し、端から端まで実行します。*正当なる職人 (The Legitimate Craftsman).* **Prometheus** (`claude-opus-4-6` / **`kimi-k2.5`** / **`glm-5`**) はあなたの戦略プランナーです。インタビューモードで動作し、コードに触れる前に質問をしてスコープを特定し、詳細な計画を構築します。 @@ -176,7 +176,7 @@ Read this and tell me why it's not just another boilerplate: https://raw.githubu > Anthropicが[私たちのせいでOpenCodeをブロックしました。](https://x.com/thdxr/status/2010149530486911014) だからこそHephaestusは「正当なる職人 (The Legitimate Craftsman)」と呼ばれているのです。皮肉を込めています。 > -> Opusで最もよく動きますが、Kimi K2.5 + GPT-5.3 Codexの組み合わせだけでも、バニラのClaude Codeを軽く凌駕します。設定は一切不要です。 +> Opusで最もよく動きますが、Kimi K2.5 + GPT-5.4の組み合わせだけでも、バニラのClaude Codeを軽く凌駕します。設定は一切不要です。 ### エージェントの��ーケストレーション diff --git a/README.ko.md b/README.ko.md index 560ea7e6b..19524e98c 100644 --- a/README.ko.md +++ b/README.ko.md @@ -162,7 +162,7 @@ Read this and tell me why it's not just another boilerplate: https://raw.githubu **Sisyphus** (`claude-opus-4-6` / **`kimi-k2.5`** / **`glm-5`**)는 당신의 메인 오케스트레이터입니다. 공격적인 병렬 실행으로 계획을 세우고, 전문가들에게 위임하며, 완료될 때까지 밀어붙입니다. 중간에 포기하는 법이 없습니다. -**Hephaestus** (`gpt-5.3-codex`)는 당신의 자율 딥 워커입니다. 레시피가 아니라 목표를 주세요. 베이비시터 없이 알아서 코드베이스를 탐색하고, 패턴을 연구하며, 끝에서 끝까지 전부 해냅니다. *진정한 장인(The Legitimate Craftsman).* +**Hephaestus** (`gpt-5.4`)는 당신의 자율 딥 워커입니다. 레시피가 아니라 목표를 주세요. 베이비시터 없이 알아서 코드베이스를 탐색하고, 패턴을 연구하며, 끝에서 끝까지 전부 해냅니다. *진정한 장인(The Legitimate Craftsman).* **Prometheus** (`claude-opus-4-6` / **`kimi-k2.5`** / **`glm-5`**)는 당신의 전략 플래너입니다. 인터뷰 모드로 작동합니다. 코드 한 줄 만지기 전에 질문을 던져 스코프를 파악하고 상세한 계획부터 세웁니다. @@ -170,7 +170,7 @@ Read this and tell me why it's not just another boilerplate: https://raw.githubu > Anthropic이 [우리 때문에 OpenCode를 막아버렸습니다.](https://x.com/thdxr/status/2010149530486911014) 그래서 Hephaestus의 별명이 "진정한 장인(The Legitimate Craftsman)"인 겁니다. (어디서 많이 들어본 이름이죠?) 아이러니를 노렸습니다. > -> Opus에서 제일 잘 돌아가긴 하지만, Kimi K2.5 + GPT-5.3 Codex 조합만으로도 바닐라 Claude Code는 가볍게 바릅니다. 설정도 필요 없습니다. +> Opus에서 제일 잘 돌아가긴 하지만, Kimi K2.5 + GPT-5.4 조합만으로도 바닐라 Claude Code는 가볍게 바릅니다. 설정도 필요 없습니다. ### 에이전트 오케스트레이션 diff --git a/README.md b/README.md index b6c500d5d..91008474c 100644 --- a/README.md +++ b/README.md @@ -164,7 +164,7 @@ Even only with following subscriptions, ultrawork will work well (this project i **Sisyphus** (`claude-opus-4-6` / **`kimi-k2.5`** / **`glm-5`** ) is your main orchestrator. He plans, delegates to specialists, and drives tasks to completion with aggressive parallel execution. He does not stop halfway. -**Hephaestus** (`gpt-5.3-codex`) is your autonomous deep worker. Give him a goal, not a recipe. He explores the codebase, researches patterns, and executes end-to-end without hand-holding. *The Legitimate Craftsman.* +**Hephaestus** (`gpt-5.4`) is your autonomous deep worker. Give him a goal, not a recipe. He explores the codebase, researches patterns, and executes end-to-end without hand-holding. *The Legitimate Craftsman.* **Prometheus** (`claude-opus-4-6` / **`kimi-k2.5`** / **`glm-5`** ) is your strategic planner. Interview mode: it questions, identifies scope, and builds a detailed plan before a single line of code is touched. @@ -172,7 +172,7 @@ Every agent is tuned to its model's specific strengths. No manual model-juggling > Anthropic [blocked OpenCode because of us.](https://x.com/thdxr/status/2010149530486911014) That's why Hephaestus is called "The Legitimate Craftsman." The irony is intentional. > -> We run best on Opus, but Kimi K2.5 + GPT-5.3 Codex already beats vanilla Claude Code. Zero config needed. +> We run best on Opus, but Kimi K2.5 + GPT-5.4 already beats vanilla Claude Code. Zero config needed. ### Agent Orchestration diff --git a/README.ru.md b/README.ru.md index d52d964b6..540643acd 100644 --- a/README.ru.md +++ b/README.ru.md @@ -152,7 +152,7 @@ Read this and tell me why it's not just another boilerplate: https://raw.githubu **Sisyphus** (`claude-opus-4-6` / **`kimi-k2.5`** / **`glm-5`**) — главный оркестратор. Он планирует, делегирует задачи специалистам и доводит их до завершения с агрессивным параллельным выполнением. Он не останавливается на полпути. -**Hephaestus** (`gpt-5.3-codex`) — автономный глубокий исполнитель. Дайте ему цель, а не рецепт. Он исследует кодовую базу, изучает паттерны и выполняет задачи сквозным образом без лишних подсказок. *Законный Мастер.* +**Hephaestus** (`gpt-5.4`) — автономный глубокий исполнитель. Дайте ему цель, а не рецепт. Он исследует кодовую базу, изучает паттерны и выполняет задачи сквозным образом без лишних подсказок. *Законный Мастер.* **Prometheus** (`claude-opus-4-6` / **`kimi-k2.5`** / **`glm-5`**) — стратегический планировщик. Режим интервью: задаёт вопросы, определяет объём работ и формирует детальный план до того, как написана хотя бы одна строка кода. @@ -160,7 +160,7 @@ Read this and tell me why it's not just another boilerplate: https://raw.githubu > Anthropic [заблокировал OpenCode из-за нас.](https://x.com/thdxr/status/2010149530486911014) Именно поэтому Hephaestus зовётся «Законным Мастером». Ирония намеренная. > -> Мы работаем лучше всего на Opus, но Kimi K2.5 + GPT-5.3 Codex уже превосходят ванильный Claude Code. Никакой настройки не требуется. +> Мы работаем лучше всего на Opus, но Kimi K2.5 + GPT-5.4 уже превосходят ванильный Claude Code. Никакой настройки не требуется. ### Оркестрация агентов diff --git a/README.zh-cn.md b/README.zh-cn.md index f0f0bd300..2b2a3ab4c 100644 --- a/README.zh-cn.md +++ b/README.zh-cn.md @@ -169,7 +169,7 @@ Read this and tell me why it's not just another boilerplate: https://raw.githubu **Sisyphus** (`claude-opus-4-6` / **`kimi-k2.5`** / **`glm-5`**) 是你的主指挥官。他负责制定计划、分配任务给专家团队,并以极其激进的并行策略推动任务直至完成。他从不半途而废。 -**Hephaestus** (`gpt-5.3-codex`) 是你的自主深度工作者。你只需要给他目标,不要给他具体做法。他会自动探索代码库模式,从头到尾独立执行任务,绝不会中途要你当保姆。*名副其实的正牌工匠。* +**Hephaestus** (`gpt-5.4`) 是你的自主深度工作者。你只需要给他目标,不要给他具体做法。他会自动探索代码库模式,从头到尾独立执行任务,绝不会中途要你当保姆。*名副其实的正牌工匠。* **Prometheus** (`claude-opus-4-6` / **`kimi-k2.5`** / **`glm-5`**) 是你的战略规划师。他通过访谈模式,在动一行代码之前,先通过提问确定范围并构建详尽的执行计划。 @@ -177,7 +177,7 @@ Read this and tell me why it's not just another boilerplate: https://raw.githubu > Anthropic [因为我们屏蔽了 OpenCode](https://x.com/thdxr/status/2010149530486911014)。这就是为什么我们将 Hephaestus 命名为“正牌工匠 (The Legitimate Craftsman)”。这是一个故意的讽刺。 > -> 我们在 Opus 上运行得最好,但仅仅使用 Kimi K2.5 + GPT-5.3 Codex 就足以碾压原版的 Claude Code。完全不需要配置。 +> 我们在 Opus 上运行得最好,但仅仅使用 Kimi K2.5 + GPT-5.4 就足以碾压原版的 Claude Code。完全不需要配置。 ### 智能体调度机制 diff --git a/docs/examples/coding-focused.jsonc b/docs/examples/coding-focused.jsonc index 631e50ccc..1eef02602 100644 --- a/docs/examples/coding-focused.jsonc +++ b/docs/examples/coding-focused.jsonc @@ -14,7 +14,7 @@ // Heavy lifter: maximum autonomy for coding tasks "hephaestus": { - "model": "openai/gpt-5.3-codex", + "model": "openai/gpt-5.4", "prompt_append": "You are the primary implementation agent. Own the codebase. Explore, decide, execute. Use LSP and AST-grep aggressively.", "permission": { "edit": "allow", "bash": { "git": "allow", "test": "allow" } }, }, diff --git a/docs/examples/default.jsonc b/docs/examples/default.jsonc index 2f357e2e4..21ec8df1b 100644 --- a/docs/examples/default.jsonc +++ b/docs/examples/default.jsonc @@ -13,7 +13,7 @@ // Deep autonomous worker: end-to-end implementation "hephaestus": { - "model": "openai/gpt-5.3-codex", + "model": "openai/gpt-5.4", "prompt_append": "Explore thoroughly, then implement. Prefer small, testable changes.", }, diff --git a/docs/examples/planning-focused.jsonc b/docs/examples/planning-focused.jsonc index 126ae10fc..4f6aef926 100644 --- a/docs/examples/planning-focused.jsonc +++ b/docs/examples/planning-focused.jsonc @@ -14,7 +14,7 @@ // Implementation: uses planning outputs "hephaestus": { - "model": "openai/gpt-5.3-codex", + "model": "openai/gpt-5.4", "prompt_append": "Follow established plans precisely. Ask for clarification when plans are ambiguous.", }, diff --git a/docs/guide/agent-model-matching.md b/docs/guide/agent-model-matching.md index a0861c540..ffcbf1b41 100644 --- a/docs/guide/agent-model-matching.md +++ b/docs/guide/agent-model-matching.md @@ -27,7 +27,7 @@ Using Sisyphus with older GPT models would be like taking your best project mana Hephaestus is the developer who stays in their room coding all day. Doesn't talk much. Might seem socially awkward. But give them a hard technical problem and they'll emerge three hours later with a solution nobody else could have found. -**This is why Hephaestus uses GPT-5.3 Codex.** Codex is built for exactly this: +**This is why Hephaestus uses GPT-5.4.** GPT-5.4 is built for exactly this: - Deep, autonomous exploration without hand-holding - Multi-file reasoning across complex codebases @@ -82,7 +82,7 @@ These agents are built for GPT's principle-driven style. Their prompts assume au | Agent | Role | Fallback Chain | Notes | | -------------- | ----------------------- | -------------------------------------- | ------------------------------------------------ | -| **Hephaestus** | Autonomous deep worker | GPT-5.3 Codex → GPT-5.4 (Copilot) | Requires GPT access. GPT-5.4 via Copilot as fallback. The craftsman. | +| **Hephaestus** | Autonomous deep worker | GPT-5.4 | Requires GPT access. The craftsman. | | **Oracle** | Architecture consultant | GPT-5.4 → Gemini 3.1 Pro → Claude Opus → opencode-go/glm-5 | Read-only high-IQ consultation. | | **Momus** | Ruthless reviewer | GPT-5.4 → Claude Opus → Gemini 3.1 Pro → opencode-go/glm-5 | Verification and plan review. GPT-5.4 uses xhigh variant. | @@ -119,7 +119,7 @@ Principle-driven, explicit reasoning, deep technical capability. Best for agents | Model | Strengths | | ----------------- | ----------------------------------------------------------------------------------------------- | -| **GPT-5.3 Codex** | Deep coding powerhouse. Autonomous exploration. Required for Hephaestus. | +| **GPT-5.3 Codex** | Deep coding powerhouse. Autonomous exploration. Still available for deep category and explicit overrides. | | **GPT-5.4** | High intelligence, strategic reasoning. Default for Oracle, Momus, and a key fallback for Prometheus / Atlas. Uses xhigh variant for Momus. | | **GPT-5.4 Mini** | Fast + strong reasoning. Good for lightweight autonomous tasks. Default for quick category. | | **GPT-5-Nano** | Ultra-cheap, fast. Good for simple utility tasks. | diff --git a/docs/guide/installation.md b/docs/guide/installation.md index 3bd1b410e..f90718d90 100644 --- a/docs/guide/installation.md +++ b/docs/guide/installation.md @@ -285,7 +285,7 @@ Not all models behave the same way. Understanding which models are "similar" hel | Model | Provider(s) | Notes | | ----------------- | -------------------------------- | ------------------------------------------------- | -| **GPT-5.3-codex** | openai, github-copilot, opencode | Deep coding powerhouse. Required for Hephaestus. | +| **GPT-5.3-codex** | openai, github-copilot, opencode | Deep coding powerhouse. Still available for deep category and explicit overrides. | | **GPT-5.4** | openai, github-copilot, opencode | High intelligence. Default for Oracle. | | **GPT-5.4 Mini** | openai, github-copilot, opencode | Fast + strong reasoning. Default for quick category. | | **GPT-5-Nano** | opencode | Ultra-cheap, fast. Good for simple utility tasks. | @@ -334,7 +334,7 @@ Priority: **Claude > GPT > Claude-like models** | Agent | Role | Default Chain | Notes | | -------------- | ---------------------- | -------------------------------------- | ------------------------------------------------------ | -| **Hephaestus** | Deep autonomous worker | GPT-5.3-codex (medium) only | "Codex on steroids." No fallback. Requires GPT access. | +| **Hephaestus** | Deep autonomous worker | GPT-5.4 (medium) only | "Codex on steroids." No fallback. Requires GPT access. | | **Oracle** | Architecture/debugging | GPT-5.4 (high) → Gemini 3.1 Pro → Opus | High-IQ strategic backup. GPT preferred. | | **Momus** | High-accuracy reviewer | GPT-5.4 (medium) → Opus → Gemini 3.1 Pro | Verification agent. GPT preferred. | diff --git a/docs/guide/orchestration.md b/docs/guide/orchestration.md index 44ef65ca9..edc0dc8e3 100644 --- a/docs/guide/orchestration.md +++ b/docs/guide/orchestration.md @@ -420,7 +420,7 @@ Atlas is automatically activated when you run `/start-work`. You don't need to m | Aspect | Hephaestus | Sisyphus + `ulw` / `ultrawork` | | --------------- | ------------------------------------------ | ---------------------------------------------------- | -| **Model** | GPT-5.3 Codex (medium reasoning) | Claude Opus 4.6 / GPT-5.4 / GLM 5 depending on setup | +| **Model** | GPT-5.4 (medium reasoning) | Claude Opus 4.6 / GPT-5.4 / GLM 5 depending on setup | | **Approach** | Autonomous deep worker | Keyword-activated ultrawork mode | | **Best For** | Complex architectural work, deep reasoning | General complex tasks, "just do it" scenarios | | **Planning** | Self-plans during execution | Uses Prometheus plans if available | @@ -443,8 +443,8 @@ Switch to Hephaestus (Tab → Select Hephaestus) when: - "Integrate our Rust core with the TypeScript frontend" - "Migrate from MongoDB to PostgreSQL with zero downtime" -4. **You specifically want GPT-5.3 Codex reasoning** - - Some problems benefit from GPT-5.3 Codex's training characteristics +4. **You specifically want GPT-5.4 reasoning** + - Some problems benefit from GPT-5.4's training characteristics **When to Use Sisyphus + `ulw`:** @@ -469,7 +469,7 @@ Use the `ulw` keyword in Sisyphus when: **Recommendation:** - **For most users**: Use `ulw` keyword in Sisyphus. It's the default path and works excellently for 90% of complex tasks. -- **For power users**: Switch to Hephaestus when you specifically need GPT-5.3 Codex's reasoning style or want the "AmpCode deep mode" experience of fully autonomous exploration and execution. +- **For power users**: Switch to Hephaestus when you specifically need GPT-5.4's reasoning style or want the "AmpCode deep mode" experience of fully autonomous exploration and execution. --- @@ -520,7 +520,7 @@ Type `exit` or start a new session. Atlas is primarily entered via `/start-work` **For most tasks**: Type `ulw` in Sisyphus. -**Use Hephaestus when**: You specifically need GPT-5.3 Codex's reasoning style for deep architectural work or complex debugging. +**Use Hephaestus when**: You specifically need GPT-5.4's reasoning style for deep architectural work or complex debugging. --- diff --git a/docs/guide/overview.md b/docs/guide/overview.md index 1d671d314..9ccf5bc4d 100644 --- a/docs/guide/overview.md +++ b/docs/guide/overview.md @@ -93,9 +93,9 @@ Sisyphus still works best on Claude-family models, Kimi, and GLM. GPT-5.4 now ha Named with intentional irony. Anthropic blocked OpenCode from using their API because of this project. So the team built an autonomous GPT-native agent instead. -Hephaestus runs on GPT-5.3 Codex. Give him a goal, not a recipe. He explores the codebase, researches patterns, and executes end-to-end without hand-holding. He is the legitimate craftsman because he was born from necessity, not privilege. +Hephaestus runs on GPT-5.4. Give him a goal, not a recipe. He explores the codebase, researches patterns, and executes end-to-end without hand-holding. He is the legitimate craftsman because he was born from necessity, not privilege. -Use Hephaestus when you need deep architectural reasoning, complex debugging across many files, or cross-domain knowledge synthesis. Switch to him explicitly when the work demands GPT-5.3 Codex's particular strengths. +Use Hephaestus when you need deep architectural reasoning, complex debugging across many files, or cross-domain knowledge synthesis. Switch to him explicitly when the work demands GPT-5.4's particular strengths. **Why this beats vanilla Codex CLI:** @@ -214,8 +214,7 @@ You can override specific agents or categories in your config: **GPT models** (explicit reasoning, principle-driven): -- GPT-5.3-codex — deep coding powerhouse, required for Hephaestus -- GPT-5.4 — high intelligence, default for Oracle +- GPT-5.4 — deep coding powerhouse, required for Hephaestus and default for Oracle - GPT-5-Nano — ultra-cheap, fast utility tasks **Different-behavior models**: diff --git a/docs/reference/configuration.md b/docs/reference/configuration.md index a927f656a..465360892 100644 --- a/docs/reference/configuration.md +++ b/docs/reference/configuration.md @@ -268,7 +268,7 @@ Disable categories: `{ "disabled_categories": ["ultrabrain"] }` | Agent | Default Model | Provider Priority | | --------------------- | ------------------- | ---------------------------------------------------------------------------- | | **Sisyphus** | `claude-opus-4-6` | `claude-opus-4-6` → `glm-5` → `big-pickle` | -| **Hephaestus** | `gpt-5.3-codex` | `gpt-5.3-codex` → `gpt-5.4` (GitHub Copilot fallback) | +| **Hephaestus** | `gpt-5.4` | `gpt-5.4` | | **oracle** | `gpt-5.4` | `gpt-5.4` → `gemini-3.1-pro` → `claude-opus-4-6` | | **librarian** | `minimax-m2.7` | `minimax-m2.7` → `minimax-m2.7-highspeed` → `claude-haiku-4-5` → `gpt-5-nano` | | **explore** | `grok-code-fast-1` | `grok-code-fast-1` → `minimax-m2.7-highspeed` → `minimax-m2.7` → `claude-haiku-4-5` → `gpt-5-nano` | diff --git a/docs/reference/features.md b/docs/reference/features.md index 63fc37204..6dbb9833f 100644 --- a/docs/reference/features.md +++ b/docs/reference/features.md @@ -9,7 +9,7 @@ Oh-My-OpenAgent provides 11 specialized AI agents. Each has distinct expertise, | Agent | Model | Purpose | | --------------------- | ------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **Sisyphus** | `claude-opus-4-6` | The default orchestrator. Plans, delegates, and executes complex tasks using specialized subagents with aggressive parallel execution. Todo-driven workflow with extended thinking (32k budget). Fallback: `glm-5` → `big-pickle`. | -| **Hephaestus** | `gpt-5.3-codex` | The Legitimate Craftsman. Autonomous deep worker inspired by AmpCode's deep mode. Goal-oriented execution with thorough research before action. Explores codebase patterns, completes tasks end-to-end without premature stopping. Named after the Greek god of forge and craftsmanship. Fallback: `gpt-5.4` on GitHub Copilot. Requires a GPT-capable provider. | +| **Hephaestus** | `gpt-5.4` | The Legitimate Craftsman. Autonomous deep worker inspired by AmpCode's deep mode. Goal-oriented execution with thorough research before action. Explores codebase patterns, completes tasks end-to-end without premature stopping. Named after the Greek god of forge and craftsmanship. Requires a GPT-capable provider. | | **Oracle** | `gpt-5.4` | Architecture decisions, code review, debugging. Read-only consultation with stellar logical reasoning and deep analysis. Inspired by AmpCode. Fallback: `gemini-3.1-pro` → `claude-opus-4-6`. | | **Librarian** | `minimax-m2.7` | Multi-repo analysis, documentation lookup, OSS implementation examples. Deep codebase understanding with evidence-based answers. Fallback: `minimax-m2.7-highspeed` → `claude-haiku-4-5` → `gpt-5-nano`. | | **Explore** | `grok-code-fast-1` | Fast codebase exploration and contextual grep. Fallback: `minimax-m2.7-highspeed` → `minimax-m2.7` → `claude-haiku-4-5` → `gpt-5-nano`. | diff --git a/src/agents/AGENTS.md b/src/agents/AGENTS.md index 49b7830f2..bc7873233 100644 --- a/src/agents/AGENTS.md +++ b/src/agents/AGENTS.md @@ -11,7 +11,7 @@ Agent factories following `createXXXAgent(model) → AgentConfig` pattern. Each | Agent | Model | Temp | Mode | Fallback Chain | Purpose | |-------|-------|------|------|----------------|---------| | **Sisyphus** | claude-opus-4-6 max | 0.1 | all | k2p5 → kimi-k2.5 → gpt-5.4 medium → glm-5 → big-pickle | Main orchestrator, plans + delegates | -| **Hephaestus** | gpt-5.3-codex medium | 0.1 | all | gpt-5.4 medium (copilot) | Autonomous deep worker | +| **Hephaestus** | gpt-5.4 medium | 0.1 | all | — | Autonomous deep worker | | **Oracle** | gpt-5.4 high | 0.1 | subagent | gemini-3.1-pro high → claude-opus-4-6 max | Read-only consultation | | **Librarian** | minimax-m2.7 | 0.1 | subagent | minimax-m2.7-highspeed → claude-haiku-4-5 → gpt-5-nano | External docs/code search | | **Explore** | grok-code-fast-1 | 0.1 | subagent | minimax-m2.7-highspeed → minimax-m2.7 → claude-haiku-4-5 → gpt-5-nano | Contextual grep | diff --git a/src/cli/__snapshots__/model-fallback.test.ts.snap b/src/cli/__snapshots__/model-fallback.test.ts.snap index 29eef9db0..1db43d5de 100644 --- a/src/cli/__snapshots__/model-fallback.test.ts.snap +++ b/src/cli/__snapshots__/model-fallback.test.ts.snap @@ -202,7 +202,7 @@ exports[`generateModelConfig single native provider uses OpenAI models when only "variant": "medium", }, "hephaestus": { - "model": "openai/gpt-5.3-codex", + "model": "openai/gpt-5.4", "variant": "medium", }, "librarian": { @@ -287,7 +287,7 @@ exports[`generateModelConfig single native provider uses OpenAI models with isMa "variant": "medium", }, "hephaestus": { - "model": "openai/gpt-5.3-codex", + "model": "openai/gpt-5.4", "variant": "medium", }, "librarian": { @@ -490,7 +490,7 @@ exports[`generateModelConfig all native providers uses preferred models from fal "model": "anthropic/claude-haiku-4-5", }, "hephaestus": { - "model": "openai/gpt-5.3-codex", + "model": "openai/gpt-5.4", "variant": "medium", }, "metis": { @@ -565,7 +565,7 @@ exports[`generateModelConfig all native providers uses preferred models with isM "model": "anthropic/claude-haiku-4-5", }, "hephaestus": { - "model": "openai/gpt-5.3-codex", + "model": "openai/gpt-5.4", "variant": "medium", }, "metis": { @@ -641,7 +641,7 @@ exports[`generateModelConfig fallback providers uses OpenCode Zen models when on "model": "opencode/claude-haiku-4-5", }, "hephaestus": { - "model": "opencode/gpt-5.3-codex", + "model": "opencode/gpt-5.4", "variant": "medium", }, "metis": { @@ -716,7 +716,7 @@ exports[`generateModelConfig fallback providers uses OpenCode Zen models with is "model": "opencode/claude-haiku-4-5", }, "hephaestus": { - "model": "opencode/gpt-5.3-codex", + "model": "opencode/gpt-5.4", "variant": "medium", }, "metis": { @@ -1049,7 +1049,7 @@ exports[`generateModelConfig mixed provider scenarios uses Claude + OpenCode Zen "model": "anthropic/claude-haiku-4-5", }, "hephaestus": { - "model": "opencode/gpt-5.3-codex", + "model": "opencode/gpt-5.4", "variant": "medium", }, "metis": { @@ -1124,7 +1124,7 @@ exports[`generateModelConfig mixed provider scenarios uses OpenAI + Copilot comb "model": "github-copilot/gpt-5-mini", }, "hephaestus": { - "model": "openai/gpt-5.3-codex", + "model": "openai/gpt-5.4", "variant": "medium", }, "metis": { @@ -1329,7 +1329,7 @@ exports[`generateModelConfig mixed provider scenarios uses all fallback provider "model": "opencode/claude-haiku-4-5", }, "hephaestus": { - "model": "opencode/gpt-5.3-codex", + "model": "github-copilot/gpt-5.4", "variant": "medium", }, "librarian": { @@ -1407,7 +1407,7 @@ exports[`generateModelConfig mixed provider scenarios uses all providers togethe "model": "anthropic/claude-haiku-4-5", }, "hephaestus": { - "model": "openai/gpt-5.3-codex", + "model": "openai/gpt-5.4", "variant": "medium", }, "librarian": { @@ -1485,7 +1485,7 @@ exports[`generateModelConfig mixed provider scenarios uses all providers with is "model": "anthropic/claude-haiku-4-5", }, "hephaestus": { - "model": "openai/gpt-5.3-codex", + "model": "openai/gpt-5.4", "variant": "medium", }, "librarian": { diff --git a/src/cli/model-fallback.test.ts b/src/cli/model-fallback.test.ts index cb6192f55..888f5336b 100644 --- a/src/cli/model-fallback.test.ts +++ b/src/cli/model-fallback.test.ts @@ -458,7 +458,7 @@ describe("generateModelConfig", () => { const result = generateModelConfig(config) // #then - expect(result.agents?.hephaestus?.model).toBe("openai/gpt-5.3-codex") + expect(result.agents?.hephaestus?.model).toBe("openai/gpt-5.4") expect(result.agents?.hephaestus?.variant).toBe("medium") }) @@ -484,7 +484,7 @@ describe("generateModelConfig", () => { const result = generateModelConfig(config) // #then - expect(result.agents?.hephaestus?.model).toBe("opencode/gpt-5.3-codex") + expect(result.agents?.hephaestus?.model).toBe("opencode/gpt-5.4") expect(result.agents?.hephaestus?.variant).toBe("medium") }) diff --git a/src/shared/agent-variant.test.ts b/src/shared/agent-variant.test.ts index 95f501c54..00ef030fb 100644 --- a/src/shared/agent-variant.test.ts +++ b/src/shared/agent-variant.test.ts @@ -113,9 +113,9 @@ describe("resolveVariantForModel", () => { }) test("returns correct variant for openai provider (hephaestus agent)", () => { - // #given hephaestus has openai/gpt-5.3-codex with variant "medium" in its chain + // #given hephaestus has openai/gpt-5.4 with variant "medium" in its chain const config = {} as OhMyOpenCodeConfig - const model = { providerID: "openai", modelID: "gpt-5.3-codex" } + const model = { providerID: "openai", modelID: "gpt-5.4" } // #when const variant = resolveVariantForModel(config, "hephaestus", model) From 1c54fdad26e9381033d539bb178ea2fa054e541c Mon Sep 17 00:00:00 2001 From: YeonGyu-Kim Date: Thu, 26 Mar 2026 19:44:55 +0900 Subject: [PATCH 56/63] =?UTF-8?q?feat(compat):=20package=20rename=20compat?= =?UTF-8?q?ibility=20layer=20for=20oh-my-opencode=20=E2=86=92=20oh-my-open?= =?UTF-8?q?agent?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add legacy plugin startup warning when oh-my-opencode config detected - Update CLI installer and TUI installer for new package name - Split monolithic config-manager.test.ts into focused test modules - Add plugin config detection tests for legacy name fallback - Update processed-command-store to use plugin-identity constants - Add claude-code-plugin-loader discovery test for both config names - Update chat-params and ultrawork-db tests for plugin identity Part of #2823 --- src/cli/cli-installer.ts | 7 +- src/cli/config-manager.test.ts | 300 ------------------ .../generate-omo-config.test.ts | 142 +++++++++ src/cli/config-manager/npm-dist-tags.test.ts | 56 ++++ .../plugin-name-with-version.test.ts | 56 ++++ .../plugin-name-with-version.ts | 3 +- src/cli/tui-installer.ts | 5 +- .../discovery.test.ts | 35 ++ .../processed-command-store.ts | 53 +++- src/index.ts | 3 +- src/plugin/chat-params.test.ts | 52 ++- .../ultrawork-db-model-override.test.ts | 23 +- src/shared/index.ts | 1 + .../log-legacy-plugin-startup-warning.test.ts | 76 +++++ .../log-legacy-plugin-startup-warning.ts | 28 ++ src/shared/plugin-config-detection.test.ts | 23 ++ 16 files changed, 526 insertions(+), 337 deletions(-) delete mode 100644 src/cli/config-manager.test.ts create mode 100644 src/cli/config-manager/generate-omo-config.test.ts create mode 100644 src/cli/config-manager/npm-dist-tags.test.ts create mode 100644 src/cli/config-manager/plugin-name-with-version.test.ts create mode 100644 src/shared/log-legacy-plugin-startup-warning.test.ts create mode 100644 src/shared/log-legacy-plugin-startup-warning.ts create mode 100644 src/shared/plugin-config-detection.test.ts diff --git a/src/cli/cli-installer.ts b/src/cli/cli-installer.ts index b97141bca..9f51eedfb 100644 --- a/src/cli/cli-installer.ts +++ b/src/cli/cli-installer.ts @@ -1,4 +1,5 @@ import color from "picocolors" +import { PLUGIN_NAME } from "../shared" import type { InstallArgs } from "./types" import { addPluginToOpenCodeConfig, @@ -32,7 +33,7 @@ export async function runCliInstaller(args: InstallArgs, version: string): Promi } console.log() printInfo( - "Usage: bunx oh-my-opencode install --no-tui --claude= --gemini= --copilot=", + `Usage: bunx ${PLUGIN_NAME} install --no-tui --claude= --gemini= --copilot=`, ) console.log() return 1 @@ -65,7 +66,7 @@ export async function runCliInstaller(args: InstallArgs, version: string): Promi const config = argsToConfig(args) - printStep(step++, totalSteps, "Adding oh-my-opencode plugin...") + printStep(step++, totalSteps, `Adding ${PLUGIN_NAME} plugin...`) const pluginResult = await addPluginToOpenCodeConfig(version) if (!pluginResult.success) { printError(`Failed: ${pluginResult.error}`) @@ -75,7 +76,7 @@ export async function runCliInstaller(args: InstallArgs, version: string): Promi `Plugin ${isUpdate ? "verified" : "added"} ${SYMBOLS.arrow} ${color.dim(pluginResult.configPath)}`, ) - printStep(step++, totalSteps, "Writing oh-my-opencode configuration...") + printStep(step++, totalSteps, `Writing ${PLUGIN_NAME} configuration...`) const omoResult = writeOmoConfig(config) if (!omoResult.success) { printError(`Failed: ${omoResult.error}`) diff --git a/src/cli/config-manager.test.ts b/src/cli/config-manager.test.ts deleted file mode 100644 index 536695a76..000000000 --- a/src/cli/config-manager.test.ts +++ /dev/null @@ -1,300 +0,0 @@ -import { describe, expect, test, mock, afterEach } from "bun:test" - -import { getPluginNameWithVersion, fetchNpmDistTags, generateOmoConfig } from "./config-manager" -import type { InstallConfig } from "./types" - -describe("getPluginNameWithVersion", () => { - const originalFetch = globalThis.fetch - - afterEach(() => { - globalThis.fetch = originalFetch - }) - - test("returns @latest when current version matches latest tag", async () => { - // #given npm dist-tags with latest=2.14.0 - globalThis.fetch = mock(() => - Promise.resolve({ - ok: true, - json: () => Promise.resolve({ latest: "2.14.0", beta: "3.0.0-beta.3" }), - } as Response) - ) as unknown as typeof fetch - - // #when current version is 2.14.0 - const result = await getPluginNameWithVersion("2.14.0") - - // #then should use @latest tag - expect(result).toBe("oh-my-opencode@latest") - }) - - test("returns @beta when current version matches beta tag", async () => { - // #given npm dist-tags with beta=3.0.0-beta.3 - globalThis.fetch = mock(() => - Promise.resolve({ - ok: true, - json: () => Promise.resolve({ latest: "2.14.0", beta: "3.0.0-beta.3" }), - } as Response) - ) as unknown as typeof fetch - - // #when current version is 3.0.0-beta.3 - const result = await getPluginNameWithVersion("3.0.0-beta.3") - - // #then should use @beta tag - expect(result).toBe("oh-my-opencode@beta") - }) - - test("returns @next when current version matches next tag", async () => { - // #given npm dist-tags with next=3.1.0-next.1 - globalThis.fetch = mock(() => - Promise.resolve({ - ok: true, - json: () => Promise.resolve({ latest: "2.14.0", beta: "3.0.0-beta.3", next: "3.1.0-next.1" }), - } as Response) - ) as unknown as typeof fetch - - // #when current version is 3.1.0-next.1 - const result = await getPluginNameWithVersion("3.1.0-next.1") - - // #then should use @next tag - expect(result).toBe("oh-my-opencode@next") - }) - - test("returns prerelease channel tag when no dist-tag matches prerelease version", async () => { - // #given npm dist-tags with beta=3.0.0-beta.3 - globalThis.fetch = mock(() => - Promise.resolve({ - ok: true, - json: () => Promise.resolve({ latest: "2.14.0", beta: "3.0.0-beta.3" }), - } as Response) - ) as unknown as typeof fetch - - // #when current version is old beta 3.0.0-beta.2 - const result = await getPluginNameWithVersion("3.0.0-beta.2") - - // #then should preserve prerelease channel - expect(result).toBe("oh-my-opencode@beta") - }) - - test("returns prerelease channel tag when fetch fails", async () => { - // #given network failure - globalThis.fetch = mock(() => Promise.reject(new Error("Network error"))) as unknown as typeof fetch - - // #when current version is 3.0.0-beta.3 - const result = await getPluginNameWithVersion("3.0.0-beta.3") - - // #then should preserve prerelease channel - expect(result).toBe("oh-my-opencode@beta") - }) - - test("returns bare package name when npm returns non-ok response for stable version", async () => { - // #given npm returns 404 - globalThis.fetch = mock(() => - Promise.resolve({ - ok: false, - status: 404, - } as Response) - ) as unknown as typeof fetch - - // #when current version is 2.14.0 - const result = await getPluginNameWithVersion("2.14.0") - - // #then should fall back to bare package entry - expect(result).toBe("oh-my-opencode") - }) - - test("prioritizes latest over other tags when version matches multiple", async () => { - // #given version matches both latest and beta (during release promotion) - globalThis.fetch = mock(() => - Promise.resolve({ - ok: true, - json: () => Promise.resolve({ beta: "3.0.0", latest: "3.0.0", next: "3.1.0-alpha.1" }), - } as Response) - ) as unknown as typeof fetch - - // #when current version matches both - const result = await getPluginNameWithVersion("3.0.0") - - // #then should prioritize @latest - expect(result).toBe("oh-my-opencode@latest") - }) -}) - -describe("fetchNpmDistTags", () => { - const originalFetch = globalThis.fetch - - afterEach(() => { - globalThis.fetch = originalFetch - }) - - test("returns dist-tags on success", async () => { - // #given npm returns dist-tags - globalThis.fetch = mock(() => - Promise.resolve({ - ok: true, - json: () => Promise.resolve({ latest: "2.14.0", beta: "3.0.0-beta.3" }), - } as Response) - ) as unknown as typeof fetch - - // #when fetching dist-tags - const result = await fetchNpmDistTags("oh-my-opencode") - - // #then should return the tags - expect(result).toEqual({ latest: "2.14.0", beta: "3.0.0-beta.3" }) - }) - - test("returns null on network failure", async () => { - // #given network failure - globalThis.fetch = mock(() => Promise.reject(new Error("Network error"))) as unknown as typeof fetch - - // #when fetching dist-tags - const result = await fetchNpmDistTags("oh-my-opencode") - - // #then should return null - expect(result).toBeNull() - }) - - test("returns null on non-ok response", async () => { - // #given npm returns 404 - globalThis.fetch = mock(() => - Promise.resolve({ - ok: false, - status: 404, - } as Response) - ) as unknown as typeof fetch - - // #when fetching dist-tags - const result = await fetchNpmDistTags("oh-my-opencode") - - // #then should return null - expect(result).toBeNull() - }) -}) - -describe("generateOmoConfig - model fallback system", () => { - test("uses github-copilot sonnet fallback when only copilot available", () => { - // #given user has only copilot (no max plan) - const config: InstallConfig = { - hasClaude: false, - isMax20: false, - hasOpenAI: false, - hasGemini: false, - hasCopilot: true, - hasOpencodeZen: false, - hasZaiCodingPlan: false, - hasKimiForCoding: false, - } - - // #when generating config - const result = generateOmoConfig(config) - - // #then Sisyphus uses Copilot (OR logic - copilot is in claude-opus-4-6 providers) - expect((result.agents as Record).sisyphus.model).toBe("github-copilot/claude-opus-4.6") - }) - - test("uses ultimate fallback when no providers configured", () => { - // #given user has no providers - const config: InstallConfig = { - hasClaude: false, - isMax20: false, - hasOpenAI: false, - hasGemini: false, - hasCopilot: false, - hasOpencodeZen: false, - hasZaiCodingPlan: false, - hasKimiForCoding: false, - } - - // #when generating config - const result = generateOmoConfig(config) - - // #then Sisyphus is omitted (requires all fallback providers) - expect(result.$schema).toBe("https://raw.githubusercontent.com/code-yeongyu/oh-my-openagent/dev/assets/oh-my-opencode.schema.json") - expect((result.agents as Record).sisyphus).toBeUndefined() - }) - - test("uses ZAI model for librarian when Z.ai is available", () => { - // #given user has Z.ai and Claude max20 - const config: InstallConfig = { - hasClaude: true, - isMax20: true, - hasOpenAI: false, - hasGemini: false, - hasCopilot: false, - hasOpencodeZen: false, - hasZaiCodingPlan: true, - hasKimiForCoding: false, - } - - // #when generating config - const result = generateOmoConfig(config) - - // #then librarian should use ZAI model - expect((result.agents as Record).librarian.model).toBe("zai-coding-plan/glm-4.7") - // #then Sisyphus uses Claude (OR logic) - expect((result.agents as Record).sisyphus.model).toBe("anthropic/claude-opus-4-6") - }) - - test("uses native OpenAI models when only ChatGPT available", () => { - // #given user has only ChatGPT subscription - const config: InstallConfig = { - hasClaude: false, - isMax20: false, - hasOpenAI: true, - hasGemini: false, - hasCopilot: false, - hasOpencodeZen: false, - hasZaiCodingPlan: false, - hasKimiForCoding: false, - } - - // #when generating config - const result = generateOmoConfig(config) - - // #then Sisyphus resolves to gpt-5.4 medium (openai is now in sisyphus chain) - expect((result.agents as Record).sisyphus.model).toBe("openai/gpt-5.4") - expect((result.agents as Record).sisyphus.variant).toBe("medium") - // #then Oracle should use native OpenAI (first fallback entry) - expect((result.agents as Record).oracle.model).toBe("openai/gpt-5.4") - // #then multimodal-looker should use native OpenAI (first fallback entry is gpt-5.4) - expect((result.agents as Record)["multimodal-looker"].model).toBe("openai/gpt-5.4") - }) - - test("uses haiku for explore when Claude max20", () => { - // #given user has Claude max20 - const config: InstallConfig = { - hasClaude: true, - isMax20: true, - hasOpenAI: false, - hasGemini: false, - hasCopilot: false, - hasOpencodeZen: false, - hasZaiCodingPlan: false, - hasKimiForCoding: false, - } - - // #when generating config - const result = generateOmoConfig(config) - - // #then explore should use haiku (max20 plan uses Claude quota) - expect((result.agents as Record).explore.model).toBe("anthropic/claude-haiku-4-5") - }) - - test("uses haiku for explore regardless of max20 flag", () => { - // #given user has Claude but not max20 - const config: InstallConfig = { - hasClaude: true, - isMax20: false, - hasOpenAI: false, - hasGemini: false, - hasCopilot: false, - hasOpencodeZen: false, - hasZaiCodingPlan: false, - hasKimiForCoding: false, - } - - // #when generating config - const result = generateOmoConfig(config) - - // #then explore should use haiku (isMax20 doesn't affect explore anymore) - expect((result.agents as Record).explore.model).toBe("anthropic/claude-haiku-4-5") - }) -}) diff --git a/src/cli/config-manager/generate-omo-config.test.ts b/src/cli/config-manager/generate-omo-config.test.ts new file mode 100644 index 000000000..2a1a24e5b --- /dev/null +++ b/src/cli/config-manager/generate-omo-config.test.ts @@ -0,0 +1,142 @@ +/// + +import { describe, expect, test } from "bun:test" + +import { generateOmoConfig } from "../config-manager" +import type { InstallConfig } from "../types" + +describe("generateOmoConfig - model fallback system", () => { + test("uses github-copilot sonnet fallback when only copilot available", () => { + //#given + const config: InstallConfig = { + hasClaude: false, + isMax20: false, + hasOpenAI: false, + hasGemini: false, + hasCopilot: true, + hasOpencodeZen: false, + hasZaiCodingPlan: false, + hasKimiForCoding: false, + hasOpencodeGo: false, + } + + //#when + const result = generateOmoConfig(config) + + //#then + expect([ + "github-copilot/claude-opus-4.6", + "github-copilot/claude-opus-4-6", + ]).toContain((result.agents as Record).sisyphus.model) + }) + + test("uses ultimate fallback when no providers configured", () => { + //#given + const config: InstallConfig = { + hasClaude: false, + isMax20: false, + hasOpenAI: false, + hasGemini: false, + hasCopilot: false, + hasOpencodeZen: false, + hasZaiCodingPlan: false, + hasKimiForCoding: false, + hasOpencodeGo: false, + } + + //#when + const result = generateOmoConfig(config) + + //#then + expect(result.$schema).toBe("https://raw.githubusercontent.com/code-yeongyu/oh-my-openagent/dev/assets/oh-my-opencode.schema.json") + expect((result.agents as Record).sisyphus).toBeUndefined() + }) + + test("uses ZAI model for librarian when Z.ai is available", () => { + //#given + const config: InstallConfig = { + hasClaude: true, + isMax20: true, + hasOpenAI: false, + hasGemini: false, + hasCopilot: false, + hasOpencodeZen: false, + hasZaiCodingPlan: true, + hasKimiForCoding: false, + hasOpencodeGo: false, + } + + //#when + const result = generateOmoConfig(config) + + //#then + expect((result.agents as Record).librarian.model).toBe("zai-coding-plan/glm-4.7") + expect((result.agents as Record).sisyphus.model).toBe("anthropic/claude-opus-4-6") + }) + + test("uses native OpenAI models when only ChatGPT available", () => { + //#given + const config: InstallConfig = { + hasClaude: false, + isMax20: false, + hasOpenAI: true, + hasGemini: false, + hasCopilot: false, + hasOpencodeZen: false, + hasZaiCodingPlan: false, + hasKimiForCoding: false, + hasOpencodeGo: false, + } + + //#when + const result = generateOmoConfig(config) + + //#then + expect((result.agents as Record).sisyphus.model).toBe("openai/gpt-5.4") + expect((result.agents as Record).sisyphus.variant).toBe("medium") + expect((result.agents as Record).oracle.model).toBe("openai/gpt-5.4") + expect((result.agents as Record)['multimodal-looker'].model).toBe("openai/gpt-5.4") + }) + + test("uses haiku for explore when Claude max20", () => { + //#given + const config: InstallConfig = { + hasClaude: true, + isMax20: true, + hasOpenAI: false, + hasGemini: false, + hasCopilot: false, + hasOpencodeZen: false, + hasZaiCodingPlan: false, + hasKimiForCoding: false, + hasOpencodeGo: false, + } + + //#when + const result = generateOmoConfig(config) + + //#then + expect((result.agents as Record).explore.model).toBe("anthropic/claude-haiku-4-5") + }) + + test("uses haiku for explore regardless of max20 flag", () => { + //#given + const config: InstallConfig = { + hasClaude: true, + isMax20: false, + hasOpenAI: false, + hasGemini: false, + hasCopilot: false, + hasOpencodeZen: false, + hasZaiCodingPlan: false, + hasKimiForCoding: false, + hasOpencodeGo: false, + } + + //#when + const result = generateOmoConfig(config) + + //#then + expect((result.agents as Record).explore.model).toBe("anthropic/claude-haiku-4-5") + }) +}) diff --git a/src/cli/config-manager/npm-dist-tags.test.ts b/src/cli/config-manager/npm-dist-tags.test.ts new file mode 100644 index 000000000..3de417290 --- /dev/null +++ b/src/cli/config-manager/npm-dist-tags.test.ts @@ -0,0 +1,56 @@ +/// + +import { afterEach, describe, expect, mock, test } from "bun:test" + +import { fetchNpmDistTags } from "../config-manager" + +describe("fetchNpmDistTags", () => { + const originalFetch = globalThis.fetch + + afterEach(() => { + globalThis.fetch = originalFetch + }) + + test("returns dist-tags on success", async () => { + //#given + globalThis.fetch = mock(() => + Promise.resolve({ + ok: true, + json: () => Promise.resolve({ latest: "3.13.1", beta: "3.14.0-beta.1" }), + } as Response) + ) as unknown as typeof fetch + + //#when + const result = await fetchNpmDistTags("oh-my-openagent") + + //#then + expect(result).toEqual({ latest: "3.13.1", beta: "3.14.0-beta.1" }) + }) + + test("returns null on network failure", async () => { + //#given + globalThis.fetch = mock(() => Promise.reject(new Error("Network error"))) as unknown as typeof fetch + + //#when + const result = await fetchNpmDistTags("oh-my-openagent") + + //#then + expect(result).toBeNull() + }) + + test("returns null on non-ok response", async () => { + //#given + globalThis.fetch = mock(() => + Promise.resolve({ + ok: false, + status: 404, + } as Response) + ) as unknown as typeof fetch + + //#when + const result = await fetchNpmDistTags("oh-my-openagent") + + //#then + expect(result).toBeNull() + }) +}) diff --git a/src/cli/config-manager/plugin-name-with-version.test.ts b/src/cli/config-manager/plugin-name-with-version.test.ts new file mode 100644 index 000000000..7da003338 --- /dev/null +++ b/src/cli/config-manager/plugin-name-with-version.test.ts @@ -0,0 +1,56 @@ +/// + +import { afterEach, describe, expect, mock, test } from "bun:test" + +import { getPluginNameWithVersion } from "../config-manager" + +describe("getPluginNameWithVersion", () => { + const originalFetch = globalThis.fetch + + afterEach(() => { + globalThis.fetch = originalFetch + }) + + test("returns the canonical latest tag when current version matches latest", async () => { + //#given + globalThis.fetch = mock(() => + Promise.resolve({ + ok: true, + json: () => Promise.resolve({ latest: "3.13.1", beta: "3.14.0-beta.1" }), + } as Response) + ) as unknown as typeof fetch + + //#when + const result = await getPluginNameWithVersion("3.13.1") + + //#then + expect(result).toBe("oh-my-openagent@latest") + }) + + test("preserves the canonical prerelease channel when fetch fails", async () => { + //#given + globalThis.fetch = mock(() => Promise.reject(new Error("Network error"))) as unknown as typeof fetch + + //#when + const result = await getPluginNameWithVersion("3.14.0-beta.1") + + //#then + expect(result).toBe("oh-my-openagent@beta") + }) + + test("returns the canonical bare package name for stable fallback", async () => { + //#given + globalThis.fetch = mock(() => + Promise.resolve({ + ok: false, + status: 404, + } as Response) + ) as unknown as typeof fetch + + //#when + const result = await getPluginNameWithVersion("3.13.1") + + //#then + expect(result).toBe("oh-my-openagent") + }) +}) diff --git a/src/cli/config-manager/plugin-name-with-version.ts b/src/cli/config-manager/plugin-name-with-version.ts index 4dfa7cf6b..a5b034278 100644 --- a/src/cli/config-manager/plugin-name-with-version.ts +++ b/src/cli/config-manager/plugin-name-with-version.ts @@ -1,6 +1,7 @@ +import { PLUGIN_NAME } from "../../shared" import { fetchNpmDistTags } from "./npm-dist-tags" -const DEFAULT_PACKAGE_NAME = "oh-my-opencode" +const DEFAULT_PACKAGE_NAME = PLUGIN_NAME const PRIORITIZED_TAGS = ["latest", "beta", "next"] as const function getFallbackEntry(version: string, packageName: string): string { diff --git a/src/cli/tui-installer.ts b/src/cli/tui-installer.ts index 410416842..49fc0c5db 100644 --- a/src/cli/tui-installer.ts +++ b/src/cli/tui-installer.ts @@ -1,5 +1,6 @@ import * as p from "@clack/prompts" import color from "picocolors" +import { PLUGIN_NAME } from "../shared" import type { InstallArgs } from "./types" import { addPluginToOpenCodeConfig, @@ -43,7 +44,7 @@ export async function runTuiInstaller(args: InstallArgs, version: string): Promi const config = await promptInstallConfig(detected) if (!config) return 1 - spinner.start("Adding oh-my-opencode to OpenCode config") + spinner.start(`Adding ${PLUGIN_NAME} to OpenCode config`) const pluginResult = await addPluginToOpenCodeConfig(version) if (!pluginResult.success) { spinner.stop(`Failed to add plugin: ${pluginResult.error}`) @@ -52,7 +53,7 @@ export async function runTuiInstaller(args: InstallArgs, version: string): Promi } spinner.stop(`Plugin added to ${color.cyan(pluginResult.configPath)}`) - spinner.start("Writing oh-my-opencode configuration") + spinner.start(`Writing ${PLUGIN_NAME} configuration`) const omoResult = writeOmoConfig(config) if (!omoResult.success) { spinner.stop(`Failed to write config: ${omoResult.error}`) diff --git a/src/features/claude-code-plugin-loader/discovery.test.ts b/src/features/claude-code-plugin-loader/discovery.test.ts index 63e2340a6..d42286579 100644 --- a/src/features/claude-code-plugin-loader/discovery.test.ts +++ b/src/features/claude-code-plugin-loader/discovery.test.ts @@ -101,4 +101,39 @@ describe("discoverInstalledPlugins", () => { expect(discovered.plugins).toHaveLength(1) expect(discovered.plugins[0]?.name).toBe("oh-my-opencode") }) + + it("derives canonical package name from npm plugin keys", () => { + //#given + const pluginsHome = process.env.CLAUDE_PLUGINS_HOME as string + const installPath = join(createTemporaryDirectory("omo-plugin-install-"), "oh-my-openagent") + mkdirSync(installPath, { recursive: true }) + + const databasePath = join(pluginsHome, "installed_plugins.json") + writeFileSync( + databasePath, + JSON.stringify({ + version: 2, + plugins: { + "oh-my-openagent@3.13.1": [ + { + scope: "user", + installPath, + version: "3.13.1", + installedAt: "2026-03-26T00:00:00Z", + lastUpdated: "2026-03-26T00:00:00Z", + }, + ], + }, + }), + "utf-8", + ) + + //#when + const discovered = discoverInstalledPlugins() + + //#then + expect(discovered.errors).toHaveLength(0) + expect(discovered.plugins).toHaveLength(1) + expect(discovered.plugins[0]?.name).toBe("oh-my-openagent") + }) }) diff --git a/src/hooks/auto-slash-command/processed-command-store.ts b/src/hooks/auto-slash-command/processed-command-store.ts index 2f6d86949..94e69428d 100644 --- a/src/hooks/auto-slash-command/processed-command-store.ts +++ b/src/hooks/auto-slash-command/processed-command-store.ts @@ -1,25 +1,36 @@ const MAX_PROCESSED_ENTRY_COUNT = 10_000 const PROCESSED_COMMAND_TTL_MS = 30_000 -function pruneExpiredEntries(entries: Map, now: number): Map { - return new Map(Array.from(entries.entries()).filter(([, expiresAt]) => expiresAt > now)) +function pruneExpiredEntries(entries: Map, now: number): void { + for (const [commandKey, expiresAt] of entries) { + if (expiresAt <= now) { + entries.delete(commandKey) + } + } } -function trimProcessedEntries(entries: Map): Map { +function trimProcessedEntries(entries: Map): void { if (entries.size <= MAX_PROCESSED_ENTRY_COUNT) { - return entries + return } - return new Map( - Array.from(entries.entries()) - .sort((left, right) => left[1] - right[1]) - .slice(Math.floor(entries.size / 2)) - ) + const targetSize = Math.floor(entries.size / 2) + for (const commandKey of entries.keys()) { + if (entries.size <= targetSize) { + return + } + + entries.delete(commandKey) + } } -function removeSessionEntries(entries: Map, sessionID: string): Map { +function removeSessionEntries(entries: Map, sessionID: string): void { const sessionPrefix = `${sessionID}:` - return new Map(Array.from(entries.entries()).filter(([entry]) => !entry.startsWith(sessionPrefix))) + for (const entry of entries.keys()) { + if (entry.startsWith(sessionPrefix)) { + entries.delete(entry) + } + } } export interface ProcessedCommandStore { @@ -34,19 +45,27 @@ export function createProcessedCommandStore(): ProcessedCommandStore { return { has(commandKey: string): boolean { - const now = Date.now() - entries = pruneExpiredEntries(entries, now) - return entries.has(commandKey) + const expiresAt = entries.get(commandKey) + if (expiresAt === undefined) { + return false + } + + if (expiresAt <= Date.now()) { + entries.delete(commandKey) + return false + } + + return true }, add(commandKey: string, ttlMs = PROCESSED_COMMAND_TTL_MS): void { const now = Date.now() - entries = pruneExpiredEntries(entries, now) + pruneExpiredEntries(entries, now) entries.delete(commandKey) entries.set(commandKey, now + ttlMs) - entries = trimProcessedEntries(entries) + trimProcessedEntries(entries) }, cleanupSession(sessionID: string): void { - entries = removeSessionEntries(entries, sessionID) + removeSessionEntries(entries, sessionID) }, clear(): void { entries.clear() diff --git a/src/index.ts b/src/index.ts index c97fedcd7..506e65c35 100644 --- a/src/index.ts +++ b/src/index.ts @@ -12,7 +12,7 @@ import { createPluginDispose, type PluginDispose } from "./plugin-dispose" import { loadPluginConfig } from "./plugin-config" import { createModelCacheState } from "./plugin-state" import { createFirstMessageVariantGate } from "./shared/first-message-variant" -import { injectServerAuthIntoClient, log } from "./shared" +import { injectServerAuthIntoClient, log, logLegacyPluginStartupWarning } from "./shared" import { startTmuxCheck } from "./tools" let activePluginDispose: PluginDispose | null = null @@ -23,6 +23,7 @@ const OhMyOpenCodePlugin: Plugin = async (ctx) => { log("[OhMyOpenCodePlugin] ENTRY - plugin loading", { directory: ctx.directory, }) + logLegacyPluginStartupWarning() injectServerAuthIntoClient(ctx.client) startTmuxCheck() diff --git a/src/plugin/chat-params.test.ts b/src/plugin/chat-params.test.ts index 622b38374..5f17f36eb 100644 --- a/src/plugin/chat-params.test.ts +++ b/src/plugin/chat-params.test.ts @@ -1,6 +1,11 @@ -import { afterEach, describe, expect, test } from "bun:test" +import { afterEach, beforeEach, describe, expect, spyOn, test } from "bun:test" +import { mkdtempSync, rmSync } from "node:fs" +import { tmpdir } from "node:os" +import { join } from "node:path" import { createChatParamsHandler, type ChatParamsOutput } from "./chat-params" +import * as dataPathModule from "../shared/data-path" +import { writeProviderModelsCache } from "../shared" import { clearSessionPromptParams, getSessionPromptParams, @@ -8,8 +13,25 @@ import { } from "../shared/session-prompt-params-state" describe("createChatParamsHandler", () => { + let tempCacheRoot = "" + let getCacheDirSpy: ReturnType + + beforeEach(() => { + tempCacheRoot = mkdtempSync(join(tmpdir(), "chat-params-cache-")) + getCacheDirSpy = spyOn(dataPathModule, "getOmoOpenCodeCacheDir").mockReturnValue( + join(tempCacheRoot, "oh-my-opencode"), + ) + writeProviderModelsCache({ connected: [], models: {} }) + }) + afterEach(() => { clearSessionPromptParams("ses_chat_params") + clearSessionPromptParams("ses_chat_params_temperature") + writeProviderModelsCache({ connected: [], models: {} }) + getCacheDirSpy?.mockRestore() + if (tempCacheRoot) { + rmSync(tempCacheRoot, { recursive: true, force: true }) + } }) test("normalizes object-style agent payload and runs chat.params hooks", async () => { @@ -31,7 +53,7 @@ describe("createChatParamsHandler", () => { message: {}, } - const output = { + const output: ChatParamsOutput = { temperature: 0.1, topP: 1, topK: 1, @@ -63,7 +85,7 @@ describe("createChatParamsHandler", () => { message, } - const output = { + const output: ChatParamsOutput = { temperature: 0.1, topP: 1, topK: 1, @@ -79,6 +101,25 @@ describe("createChatParamsHandler", () => { test("applies stored prompt params for the session", async () => { //#given + writeProviderModelsCache({ + connected: ["openai"], + models: { + openai: [ + { + id: "gpt-5.4", + name: "GPT-5.4", + temperature: true, + reasoning: true, + variants: { + low: {}, + high: {}, + }, + limit: { output: 128_000 }, + }, + ], + }, + }) + setSessionPromptParams("ses_chat_params_temperature", { temperature: 0.4, topP: 0.7, @@ -134,7 +175,7 @@ describe("createChatParamsHandler", () => { }) }) - test("preserves gpt-5.4 temperature and clamps maxTokens from bundled model capabilities", async () => { + test("drops gpt-5.4 temperature and clamps maxTokens from bundled model capabilities", async () => { //#given setSessionPromptParams("ses_chat_params_temperature", { temperature: 0.7, @@ -155,7 +196,7 @@ describe("createChatParamsHandler", () => { message: {}, } - const output = { + const output: ChatParamsOutput = { temperature: 0.1, topP: 1, topK: 1, @@ -167,7 +208,6 @@ describe("createChatParamsHandler", () => { //#then expect(output).toEqual({ - temperature: 0.7, topP: 1, topK: 1, options: { diff --git a/src/plugin/ultrawork-db-model-override.test.ts b/src/plugin/ultrawork-db-model-override.test.ts index db364a97b..a5b350e75 100644 --- a/src/plugin/ultrawork-db-model-override.test.ts +++ b/src/plugin/ultrawork-db-model-override.test.ts @@ -22,6 +22,10 @@ function flushWithTimeout(): Promise { return new Promise((resolve) => setTimeout(resolve, 10)) } +function isRecord(value: unknown): value is Record { + return typeof value === "object" && value !== null +} + describe("scheduleDeferredModelOverride", () => { let tempDir: string let dbPath: string @@ -60,9 +64,7 @@ describe("scheduleDeferredModelOverride", () => { const db = new Database(dbPath) db.run( `INSERT INTO message (id, session_id, data) VALUES (?, ?, ?)`, - id, - "ses_test", - JSON.stringify({ model }), + [id, "ses_test", JSON.stringify({ model })], ) db.close() } @@ -178,7 +180,7 @@ describe("scheduleDeferredModelOverride", () => { ) }) - test("should not crash when DB file exists but is corrupted", async () => { + test("should log a DB failure when DB file exists but is corrupted", async () => { //#given const { chmodSync, writeFileSync } = await import("node:fs") const corruptedDbPath = join(tempDir, "opencode", "opencode.db") @@ -194,9 +196,16 @@ describe("scheduleDeferredModelOverride", () => { await flushMicrotasks(5) //#then - expect(logSpy).toHaveBeenCalledWith( - expect.stringContaining("Failed to open DB"), - expect.objectContaining({ messageId: "msg_corrupt" }), + const failureCall = logSpy.mock.calls.find(([message, metadata]) => + typeof message === "string" + && ( + message.includes("Failed to open DB") + || message.includes("Deferred DB update failed with error") + ) + && isRecord(metadata) + && metadata.messageId === "msg_corrupt" ) + + expect(failureCall).toBeDefined() }) }) diff --git a/src/shared/index.ts b/src/shared/index.ts index ee690e816..e178952b5 100644 --- a/src/shared/index.ts +++ b/src/shared/index.ts @@ -70,3 +70,4 @@ export * from "./internal-initiator-marker" export * from "./plugin-command-discovery" export { SessionCategoryRegistry } from "./session-category-registry" export * from "./plugin-identity" +export * from "./log-legacy-plugin-startup-warning" diff --git a/src/shared/log-legacy-plugin-startup-warning.test.ts b/src/shared/log-legacy-plugin-startup-warning.test.ts new file mode 100644 index 000000000..f4a23786c --- /dev/null +++ b/src/shared/log-legacy-plugin-startup-warning.test.ts @@ -0,0 +1,76 @@ +import { beforeEach, describe, expect, it, mock } from "bun:test" +import type { LegacyPluginCheckResult } from "./legacy-plugin-warning" + +function createLegacyPluginCheckResult( + overrides: Partial = {}, +): LegacyPluginCheckResult { + return { + hasLegacyEntry: false, + hasCanonicalEntry: false, + legacyEntries: [], + ...overrides, + } +} + +const mockCheckForLegacyPluginEntry = mock(() => createLegacyPluginCheckResult()) + +const mockLog = mock(() => {}) + +mock.module("./legacy-plugin-warning", () => ({ + checkForLegacyPluginEntry: mockCheckForLegacyPluginEntry, +})) + +mock.module("./logger", () => ({ + log: mockLog, +})) + +async function importFreshStartupWarningModule(): Promise { + return import(`./log-legacy-plugin-startup-warning?test=${Date.now()}-${Math.random()}`) +} + +describe("logLegacyPluginStartupWarning", () => { + beforeEach(() => { + mockCheckForLegacyPluginEntry.mockReset() + mockLog.mockReset() + + mockCheckForLegacyPluginEntry.mockReturnValue(createLegacyPluginCheckResult()) + }) + + describe("#given OpenCode config contains legacy plugin entries", () => { + it("logs the legacy entries with canonical replacements", async () => { + //#given + mockCheckForLegacyPluginEntry.mockReturnValue(createLegacyPluginCheckResult({ + hasLegacyEntry: true, + legacyEntries: ["oh-my-opencode", "oh-my-opencode@3.13.1"], + })) + const { logLegacyPluginStartupWarning } = await importFreshStartupWarningModule() + + //#when + logLegacyPluginStartupWarning() + + //#then + expect(mockLog).toHaveBeenCalledTimes(1) + expect(mockLog).toHaveBeenCalledWith( + "[OhMyOpenCodePlugin] Legacy plugin entry detected in OpenCode config", + { + legacyEntries: ["oh-my-opencode", "oh-my-opencode@3.13.1"], + suggestedEntries: ["oh-my-openagent", "oh-my-openagent@3.13.1"], + hasCanonicalEntry: false, + }, + ) + }) + }) + + describe("#given OpenCode config uses only canonical plugin entries", () => { + it("does not log a startup warning", async () => { + //#given + const { logLegacyPluginStartupWarning } = await importFreshStartupWarningModule() + + //#when + logLegacyPluginStartupWarning() + + //#then + expect(mockLog).not.toHaveBeenCalled() + }) + }) +}) diff --git a/src/shared/log-legacy-plugin-startup-warning.ts b/src/shared/log-legacy-plugin-startup-warning.ts new file mode 100644 index 000000000..f5712a2e9 --- /dev/null +++ b/src/shared/log-legacy-plugin-startup-warning.ts @@ -0,0 +1,28 @@ +import { checkForLegacyPluginEntry } from "./legacy-plugin-warning" +import { log } from "./logger" +import { LEGACY_PLUGIN_NAME, PLUGIN_NAME } from "./plugin-identity" + +function toCanonicalEntry(entry: string): string { + if (entry === LEGACY_PLUGIN_NAME) { + return PLUGIN_NAME + } + + if (entry.startsWith(`${LEGACY_PLUGIN_NAME}@`)) { + return `${PLUGIN_NAME}${entry.slice(LEGACY_PLUGIN_NAME.length)}` + } + + return entry +} + +export function logLegacyPluginStartupWarning(): void { + const result = checkForLegacyPluginEntry() + if (!result.hasLegacyEntry) { + return + } + + log("[OhMyOpenCodePlugin] Legacy plugin entry detected in OpenCode config", { + legacyEntries: result.legacyEntries, + suggestedEntries: result.legacyEntries.map(toCanonicalEntry), + hasCanonicalEntry: result.hasCanonicalEntry, + }) +} diff --git a/src/shared/plugin-config-detection.test.ts b/src/shared/plugin-config-detection.test.ts new file mode 100644 index 000000000..34ad9b434 --- /dev/null +++ b/src/shared/plugin-config-detection.test.ts @@ -0,0 +1,23 @@ +import { describe, expect, test } from "bun:test" +import { existsSync, mkdirSync, rmSync, writeFileSync } from "node:fs" +import { join } from "node:path" +import { detectPluginConfigFile } from "./jsonc-parser" + +describe("detectPluginConfigFile - canonical config detection", () => { + const testDir = join(__dirname, ".test-detect-plugin-canonical") + + test("detects oh-my-openagent config when no legacy config exists", () => { + //#given + if (!existsSync(testDir)) mkdirSync(testDir, { recursive: true }) + writeFileSync(join(testDir, "oh-my-openagent.jsonc"), "{}") + + //#when + const result = detectPluginConfigFile(testDir) + + //#then + expect(result.format).toBe("jsonc") + expect(result.path).toBe(join(testDir, "oh-my-openagent.jsonc")) + + rmSync(testDir, { recursive: true, force: true }) + }) +}) From f419a3a925352b50a2615c60d9c4f64c6173a8a6 Mon Sep 17 00:00:00 2001 From: YeonGyu-Kim Date: Thu, 26 Mar 2026 19:47:25 +0900 Subject: [PATCH 57/63] fix(test): use Bun.spawnSync in command discovery test to avoid execFileSync mock leakage The opencode-project-command-discovery test used execFileSync for git init, which collided with image-converter.test.ts's global execFileSync mock when running in parallel on Linux CI. Switching to Bun.spawnSync avoids the mock entirely since spyOn(childProcess, 'execFileSync') doesn't affect Bun APIs. Fixes CI flake that only reproduced on Linux. --- .../opencode-project-command-discovery.test.ts | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/tools/slashcommand/opencode-project-command-discovery.test.ts b/src/tools/slashcommand/opencode-project-command-discovery.test.ts index f845d9b93..3192564bd 100644 --- a/src/tools/slashcommand/opencode-project-command-discovery.test.ts +++ b/src/tools/slashcommand/opencode-project-command-discovery.test.ts @@ -1,4 +1,3 @@ -import { execFileSync } from "node:child_process" import { afterEach, beforeEach, describe, expect, it } from "bun:test" import { mkdtempSync, mkdirSync, rmSync, writeFileSync } from "node:fs" import { tmpdir } from "node:os" @@ -27,9 +26,12 @@ describe("opencode project command discovery", () => { const nestedDirectory = join(repositoryDir, "packages", "app", "src") mkdirSync(nestedDirectory, { recursive: true }) - execFileSync("git", ["init"], { + // Use Bun.spawnSync instead of execFileSync to avoid mock leakage + // from parallel test files (e.g. image-converter.test.ts mocks execFileSync globally) + Bun.spawnSync(["git", "init"], { cwd: repositoryDir, - stdio: ["ignore", "ignore", "ignore"], + stdout: "ignore", + stderr: "ignore", }) writeCommand( From 8e65d6cf2c3fcaee7d28782599787f7fc2f60e14 Mon Sep 17 00:00:00 2001 From: YeonGyu-Kim Date: Thu, 26 Mar 2026 19:54:05 +0900 Subject: [PATCH 58/63] fix(test): make legacy-plugin-warning tests isolation-safe Pass explicit config dir to checkForLegacyPluginEntry instead of relying on XDG_CONFIG_HOME env var, which gets contaminated by parallel tests on Linux CI. Also adds missing 'join' import. --- src/shared/legacy-plugin-warning.test.ts | 40 +++++++----------------- src/shared/legacy-plugin-warning.ts | 15 +++++++-- 2 files changed, 24 insertions(+), 31 deletions(-) diff --git a/src/shared/legacy-plugin-warning.test.ts b/src/shared/legacy-plugin-warning.test.ts index adbfcfa8b..3bc16c3b1 100644 --- a/src/shared/legacy-plugin-warning.test.ts +++ b/src/shared/legacy-plugin-warning.test.ts @@ -6,40 +6,22 @@ import { checkForLegacyPluginEntry } from "./legacy-plugin-warning" describe("checkForLegacyPluginEntry", () => { let testConfigDir = "" - let originalXdgConfigHome: string | undefined - let originalOpenCodeConfigDir: string | undefined beforeEach(() => { - originalXdgConfigHome = process.env.XDG_CONFIG_HOME - originalOpenCodeConfigDir = process.env.OPENCODE_CONFIG_DIR testConfigDir = join(tmpdir(), `omo-legacy-check-${Date.now()}-${Math.random().toString(36).slice(2)}`) - mkdirSync(join(testConfigDir, "opencode"), { recursive: true }) - process.env.XDG_CONFIG_HOME = testConfigDir - delete process.env.OPENCODE_CONFIG_DIR + mkdirSync(testConfigDir, { recursive: true }) }) afterEach(() => { - if (originalXdgConfigHome === undefined) { - delete process.env.XDG_CONFIG_HOME - } else { - process.env.XDG_CONFIG_HOME = originalXdgConfigHome - } - - if (originalOpenCodeConfigDir === undefined) { - delete process.env.OPENCODE_CONFIG_DIR - } else { - process.env.OPENCODE_CONFIG_DIR = originalOpenCodeConfigDir - } - rmSync(testConfigDir, { recursive: true, force: true }) }) it("detects a bare legacy plugin entry", () => { // given - writeFileSync(join(testConfigDir, "opencode", "opencode.json"), JSON.stringify({ plugin: ["oh-my-opencode"] }, null, 2)) + writeFileSync(join(testConfigDir, "opencode.json"), JSON.stringify({ plugin: ["oh-my-opencode"] }, null, 2)) // when - const result = checkForLegacyPluginEntry() + const result = checkForLegacyPluginEntry(testConfigDir) // then expect(result.hasLegacyEntry).toBe(true) @@ -49,10 +31,10 @@ describe("checkForLegacyPluginEntry", () => { it("detects a version-pinned legacy plugin entry", () => { // given - writeFileSync(join(testConfigDir, "opencode", "opencode.json"), JSON.stringify({ plugin: ["oh-my-opencode@3.10.0"] }, null, 2)) + writeFileSync(join(testConfigDir, "opencode.json"), JSON.stringify({ plugin: ["oh-my-opencode@3.10.0"] }, null, 2)) // when - const result = checkForLegacyPluginEntry() + const result = checkForLegacyPluginEntry(testConfigDir) // then expect(result.hasLegacyEntry).toBe(true) @@ -62,10 +44,10 @@ describe("checkForLegacyPluginEntry", () => { it("does not flag a canonical plugin entry", () => { // given - writeFileSync(join(testConfigDir, "opencode", "opencode.json"), JSON.stringify({ plugin: ["oh-my-openagent"] }, null, 2)) + writeFileSync(join(testConfigDir, "opencode.json"), JSON.stringify({ plugin: ["oh-my-openagent"] }, null, 2)) // when - const result = checkForLegacyPluginEntry() + const result = checkForLegacyPluginEntry(testConfigDir) // then expect(result.hasLegacyEntry).toBe(false) @@ -75,10 +57,10 @@ describe("checkForLegacyPluginEntry", () => { it("detects legacy entries in quoted jsonc config", () => { // given - writeFileSync(join(testConfigDir, "opencode", "opencode.jsonc"), '{\n "plugin": ["oh-my-opencode"]\n}\n') + writeFileSync(join(testConfigDir, "opencode.jsonc"), '{\n "plugin": ["oh-my-opencode"]\n}\n') // when - const result = checkForLegacyPluginEntry() + const result = checkForLegacyPluginEntry(testConfigDir) // then expect(result.hasLegacyEntry).toBe(true) @@ -86,8 +68,10 @@ describe("checkForLegacyPluginEntry", () => { }) it("returns no warning data when config is missing", () => { + // given — empty dir, no config files + // when - const result = checkForLegacyPluginEntry() + const result = checkForLegacyPluginEntry(testConfigDir) // then expect(result.hasLegacyEntry).toBe(false) diff --git a/src/shared/legacy-plugin-warning.ts b/src/shared/legacy-plugin-warning.ts index 5e97a764f..c8a7e94df 100644 --- a/src/shared/legacy-plugin-warning.ts +++ b/src/shared/legacy-plugin-warning.ts @@ -1,4 +1,5 @@ import { existsSync, readFileSync } from "node:fs" +import { join } from "node:path" import { parseJsoncSafe } from "./jsonc-parser" import { getOpenCodeConfigPaths } from "./opencode-config-dir" @@ -14,7 +15,15 @@ export interface LegacyPluginCheckResult { legacyEntries: string[] } -function getOpenCodeConfigPath(): string | null { +function getOpenCodeConfigPath(overrideConfigDir?: string): string | null { + if (overrideConfigDir) { + const jsonPath = join(overrideConfigDir, "opencode.json") + const jsoncPath = join(overrideConfigDir, "opencode.jsonc") + if (existsSync(jsoncPath)) return jsoncPath + if (existsSync(jsonPath)) return jsonPath + return null + } + const { configJsonc, configJson } = getOpenCodeConfigPaths({ binary: "opencode", version: null }) if (existsSync(configJsonc)) return configJsonc @@ -30,8 +39,8 @@ function isCanonicalPluginEntry(entry: string): boolean { return entry === PLUGIN_NAME || entry.startsWith(`${PLUGIN_NAME}@`) } -export function checkForLegacyPluginEntry(): LegacyPluginCheckResult { - const configPath = getOpenCodeConfigPath() +export function checkForLegacyPluginEntry(overrideConfigDir?: string): LegacyPluginCheckResult { + const configPath = getOpenCodeConfigPath(overrideConfigDir) if (!configPath) { return { hasLegacyEntry: false, hasCanonicalEntry: false, legacyEntries: [] } } From 3e13a4cf57a445059f86e39b4577e9e5c362aebe Mon Sep 17 00:00:00 2001 From: YeonGyu-Kim Date: Thu, 26 Mar 2026 20:48:33 +0900 Subject: [PATCH 59/63] fix(session-recovery): filter invalid prt_* part IDs from tool_use_id reconstruction When recovering missing tool results, the session recovery hook was using raw part.id (prt_* format) as tool_use_id when callID was absent, causing ZodError validation failures from the API. Added isValidToolUseID() guard that only accepts toolu_* and call_* prefixed IDs, and normalizeMessagePart() that returns null for parts without valid callIDs. Both the SQLite fallback and stored-parts paths now filter out invalid entries before constructing tool_result payloads. Includes 4 regression tests covering both valid/invalid callID paths for both SQLite and stored-parts backends. --- .../recover-tool-result-missing.test.ts | 134 ++++++++++++++++++ .../recover-tool-result-missing.ts | 34 +++-- 2 files changed, 159 insertions(+), 9 deletions(-) create mode 100644 src/hooks/session-recovery/recover-tool-result-missing.test.ts diff --git a/src/hooks/session-recovery/recover-tool-result-missing.test.ts b/src/hooks/session-recovery/recover-tool-result-missing.test.ts new file mode 100644 index 000000000..eac10fdd4 --- /dev/null +++ b/src/hooks/session-recovery/recover-tool-result-missing.test.ts @@ -0,0 +1,134 @@ +const { describe, it, expect, mock, beforeEach } = require("bun:test") + +import type { MessageData } from "./types" + +let sqliteBackend = false +let storedParts: Array<{ type: string; id?: string; callID?: string; [key: string]: unknown }> = [] + +mock.module("../../shared/opencode-storage-detection", () => ({ + isSqliteBackend: () => sqliteBackend, +})) + +mock.module("../../shared", () => ({ + normalizeSDKResponse: (response: { data?: TData }, fallback: TData): TData => response.data ?? fallback, +})) + +mock.module("./storage", () => ({ + readParts: () => storedParts, +})) + +const { recoverToolResultMissing } = await import("./recover-tool-result-missing") + +function createMockClient(messages: MessageData[] = []) { + const promptAsync = mock(() => Promise.resolve({})) + + return { + client: { + session: { + messages: mock(() => Promise.resolve({ data: messages })), + promptAsync, + }, + } as never, + promptAsync, + } +} + +const failedAssistantMsg: MessageData = { + info: { id: "msg_failed", role: "assistant" }, + parts: [], +} + +describe("recoverToolResultMissing", () => { + beforeEach(() => { + sqliteBackend = false + storedParts = [] + }) + + it("returns false for sqlite fallback when tool part has no valid callID", async () => { + //#given + sqliteBackend = true + const { client, promptAsync } = createMockClient([ + { + info: { id: "msg_failed", role: "assistant" }, + parts: [{ type: "tool", id: "prt_missing_call", name: "bash", input: {} }], + }, + ]) + + //#when + const result = await recoverToolResultMissing(client, "ses_1", failedAssistantMsg) + + //#then + expect(result).toBe(false) + expect(promptAsync).not.toHaveBeenCalled() + }) + + it("sends the recovered sqlite tool result when callID is valid", async () => { + //#given + sqliteBackend = true + const { client, promptAsync } = createMockClient([ + { + info: { id: "msg_failed", role: "assistant" }, + parts: [{ type: "tool", id: "prt_valid_call", callID: "call_recovered", name: "bash", input: {} }], + }, + ]) + + //#when + const result = await recoverToolResultMissing(client, "ses_1", failedAssistantMsg) + + //#then + expect(result).toBe(true) + expect(promptAsync).toHaveBeenCalledWith({ + path: { id: "ses_1" }, + body: { + parts: [{ + type: "tool_result", + tool_use_id: "call_recovered", + content: "Operation cancelled by user (ESC pressed)", + }], + }, + }) + }) + + it("returns false for stored parts when tool part has no valid callID", async () => { + //#given + storedParts = [{ type: "tool", id: "prt_stored_missing_call", tool: "bash", state: { input: {} } }] + const { client, promptAsync } = createMockClient() + + //#when + const result = await recoverToolResultMissing(client, "ses_2", failedAssistantMsg) + + //#then + expect(result).toBe(false) + expect(promptAsync).not.toHaveBeenCalled() + }) + + it("sends the recovered stored tool result when callID is valid", async () => { + //#given + storedParts = [{ + type: "tool", + id: "prt_stored_valid_call", + callID: "toolu_recovered", + tool: "bash", + state: { input: {} }, + }] + const { client, promptAsync } = createMockClient() + + //#when + const result = await recoverToolResultMissing(client, "ses_2", failedAssistantMsg) + + //#then + expect(result).toBe(true) + expect(promptAsync).toHaveBeenCalledWith({ + path: { id: "ses_2" }, + body: { + parts: [{ + type: "tool_result", + tool_use_id: "toolu_recovered", + content: "Operation cancelled by user (ESC pressed)", + }], + }, + }) + }) +}) + +export {} diff --git a/src/hooks/session-recovery/recover-tool-result-missing.ts b/src/hooks/session-recovery/recover-tool-result-missing.ts index 4d19880b3..c3d12da53 100644 --- a/src/hooks/session-recovery/recover-tool-result-missing.ts +++ b/src/hooks/session-recovery/recover-tool-result-missing.ts @@ -24,8 +24,30 @@ interface MessagePart { id?: string } +function isValidToolUseID(id: string | undefined): id is string { + return typeof id === "string" && /^(toolu_|call_)/.test(id) +} + +function normalizeMessagePart(part: { type: string; id?: string; callID?: string }): MessagePart | null { + if (part.type === "tool" || part.type === "tool_use") { + if (!isValidToolUseID(part.callID)) { + return null + } + + return { + type: "tool_use", + id: part.callID, + } + } + + return { + type: part.type, + id: part.id, + } +} + function extractToolUseIds(parts: MessagePart[]): string[] { - return parts.filter((part): part is ToolUsePart => part.type === "tool_use" && !!part.id).map((part) => part.id) + return parts.filter((part): part is ToolUsePart => part.type === "tool_use" && isValidToolUseID(part.id)).map((part) => part.id) } async function readPartsFromSDKFallback( @@ -39,10 +61,7 @@ async function readPartsFromSDKFallback( const target = messages.find((m) => m.info?.id === messageID) if (!target?.parts) return [] - return target.parts.map((part) => ({ - type: part.type === "tool" ? "tool_use" : part.type, - id: "callID" in part ? (part as { callID?: string }).callID : part.id, - })) + return target.parts.map((part) => normalizeMessagePart(part)).filter((part): part is MessagePart => part !== null) } catch { return [] } @@ -59,10 +78,7 @@ export async function recoverToolResultMissing( parts = await readPartsFromSDKFallback(client, sessionID, failedAssistantMsg.info.id) } else { const storedParts = readParts(failedAssistantMsg.info.id) - parts = storedParts.map((part) => ({ - type: part.type === "tool" ? "tool_use" : part.type, - id: "callID" in part ? (part as { callID?: string }).callID : part.id, - })) + parts = storedParts.map((part) => normalizeMessagePart(part)).filter((part): part is MessagePart => part !== null) } } From 9daaeedc507761ac68a88c2add5805fa64d2d7e6 Mon Sep 17 00:00:00 2001 From: YeonGyu-Kim Date: Fri, 27 Mar 2026 00:08:20 +0900 Subject: [PATCH 60/63] fix(test): restore shared Bun mocks after suite cleanup Prevent src/shared batch runs from leaking module mocks into later files, which was breaking Linux CI cache metadata and legacy plugin warning assertions. --- src/shared/log-legacy-plugin-startup-warning.test.ts | 6 +++++- src/shared/model-capabilities.test.ts | 6 +++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/src/shared/log-legacy-plugin-startup-warning.test.ts b/src/shared/log-legacy-plugin-startup-warning.test.ts index f4a23786c..5259515b8 100644 --- a/src/shared/log-legacy-plugin-startup-warning.test.ts +++ b/src/shared/log-legacy-plugin-startup-warning.test.ts @@ -1,4 +1,4 @@ -import { beforeEach, describe, expect, it, mock } from "bun:test" +import { afterAll, beforeEach, describe, expect, it, mock } from "bun:test" import type { LegacyPluginCheckResult } from "./legacy-plugin-warning" function createLegacyPluginCheckResult( @@ -24,6 +24,10 @@ mock.module("./logger", () => ({ log: mockLog, })) +afterAll(() => { + mock.restore() +}) + async function importFreshStartupWarningModule(): Promise { return import(`./log-legacy-plugin-startup-warning?test=${Date.now()}-${Math.random()}`) } diff --git a/src/shared/model-capabilities.test.ts b/src/shared/model-capabilities.test.ts index 8532ace04..80747e333 100644 --- a/src/shared/model-capabilities.test.ts +++ b/src/shared/model-capabilities.test.ts @@ -1,4 +1,4 @@ -import { describe, expect, test, mock } from "bun:test" +import { afterAll, describe, expect, test, mock } from "bun:test" // Mock connected-providers-cache to prevent local disk cache from polluting test results. // Without this, findProviderModelMetadata reads real cached model metadata (e.g., from opencode serve) @@ -10,6 +10,10 @@ mock.module("./connected-providers-cache", () => ({ hasProviderModelsCache: () => false, })) +afterAll(() => { + mock.restore() +}) + import { getModelCapabilities, getBundledModelCapabilitiesSnapshot, From 8dd0191ea5ac1a9d9abefe48eddc09dff95b5721 Mon Sep 17 00:00:00 2001 From: YeonGyu-Kim Date: Fri, 27 Mar 2026 00:08:27 +0900 Subject: [PATCH 61/63] fix(ci): isolate mock-heavy shared tests to prevent cross-file contamination Move 4 src/shared tests that use mock.module() to the isolated test section: - model-capabilities.test.ts (mocks ./connected-providers-cache) - log-legacy-plugin-startup-warning.test.ts (mocks ./legacy-plugin-warning) - model-error-classifier.test.ts - opencode-message-dir.test.ts Also isolate recover-tool-result-missing.test.ts (mocks ./storage). Use find + exclusion pattern in remaining tests to dynamically build the src/shared file list without the isolated mock-heavy files. Fixes 6 Linux CI failures caused by bun's mock.module() cache pollution when running in parallel. --- .github/workflows/ci.yml | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 7639338d7..fe8a0f11b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -60,16 +60,31 @@ jobs: bun test src/features/opencode-skill-loader/loader.test.ts bun test src/hooks/anthropic-context-window-limit-recovery/recovery-hook.test.ts bun test src/hooks/anthropic-context-window-limit-recovery/executor.test.ts + # src/shared mock-heavy files (mock.module pollutes connected-providers-cache and legacy-plugin-warning) + bun test src/shared/model-capabilities.test.ts + bun test src/shared/log-legacy-plugin-startup-warning.test.ts + bun test src/shared/model-error-classifier.test.ts + bun test src/shared/opencode-message-dir.test.ts + # session-recovery mock isolation (recover-tool-result-missing mocks ./storage) + bun test src/hooks/session-recovery/recover-tool-result-missing.test.ts - name: Run remaining tests run: | # Enumerate subdirectories/files explicitly to EXCLUDE mock-heavy files # that were already run in isolation above. + # Excluded from src/shared: model-capabilities, log-legacy-plugin-startup-warning, model-error-classifier, opencode-message-dir # Excluded from src/cli: doctor/formatter.test.ts, doctor/format-default.test.ts # Excluded from src/tools: call-omo-agent/sync-executor.test.ts, call-omo-agent/session-creator.test.ts, session-manager (all) # Excluded from src/hooks/anthropic-context-window-limit-recovery: recovery-hook.test.ts, executor.test.ts + # Build src/shared file list excluding mock-heavy files already run in isolation + SHARED_FILES=$(find src/shared -name '*.test.ts' \ + ! -name 'model-capabilities.test.ts' \ + ! -name 'log-legacy-plugin-startup-warning.test.ts' \ + ! -name 'model-error-classifier.test.ts' \ + ! -name 'opencode-message-dir.test.ts' \ + | sort | tr '\n' ' ') bun test bin script src/config src/mcp src/index.test.ts \ - src/agents src/shared \ + src/agents $SHARED_FILES \ src/cli/run src/cli/config-manager src/cli/mcp-oauth \ src/cli/index.test.ts src/cli/install.test.ts src/cli/model-fallback.test.ts \ src/cli/config-manager.test.ts \ @@ -82,6 +97,7 @@ jobs: src/tools/call-omo-agent/background-executor.test.ts \ src/tools/call-omo-agent/subagent-session-creator.test.ts \ src/hooks/anthropic-context-window-limit-recovery/empty-content-recovery-sdk.test.ts src/hooks/anthropic-context-window-limit-recovery/parser.test.ts src/hooks/anthropic-context-window-limit-recovery/pruning-deduplication.test.ts src/hooks/anthropic-context-window-limit-recovery/recovery-deduplication.test.ts src/hooks/anthropic-context-window-limit-recovery/storage.test.ts \ + src/hooks/session-recovery/detect-error-type.test.ts src/hooks/session-recovery/index.test.ts src/hooks/session-recovery/recover-empty-content-message-sdk.test.ts src/hooks/session-recovery/resume.test.ts src/hooks/session-recovery/storage \ src/hooks/claude-code-compatibility \ src/hooks/context-injection \ src/hooks/provider-toast \ From 1c9f4148d000891bd3e8c089cea720eb16c9799b Mon Sep 17 00:00:00 2001 From: YeonGyu-Kim Date: Fri, 27 Mar 2026 00:56:55 +0900 Subject: [PATCH 62/63] fix(publish-ci): sync mock-heavy test isolation with ci.yml Apply the same mock.module() isolation fixes to publish.yml: - Move shared and session-recovery mock-heavy tests to isolated section - Use dynamic find + exclusion for remaining src/shared tests - Include session-recovery tests in remaining batch Ensures publish workflow has the same test config as main CI run. --- .github/workflows/publish.yml | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 8a6f6e50d..1c90014f7 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -61,16 +61,31 @@ jobs: bun test src/features/opencode-skill-loader/loader.test.ts bun test src/hooks/anthropic-context-window-limit-recovery/recovery-hook.test.ts bun test src/hooks/anthropic-context-window-limit-recovery/executor.test.ts + # src/shared mock-heavy files (mock.module pollutes connected-providers-cache and legacy-plugin-warning) + bun test src/shared/model-capabilities.test.ts + bun test src/shared/log-legacy-plugin-startup-warning.test.ts + bun test src/shared/model-error-classifier.test.ts + bun test src/shared/opencode-message-dir.test.ts + # session-recovery mock isolation (recover-tool-result-missing mocks ./storage) + bun test src/hooks/session-recovery/recover-tool-result-missing.test.ts - name: Run remaining tests run: | # Enumerate subdirectories/files explicitly to EXCLUDE mock-heavy files # that were already run in isolation above. + # Excluded from src/shared: model-capabilities, log-legacy-plugin-startup-warning, model-error-classifier, opencode-message-dir # Excluded from src/cli: doctor/formatter.test.ts, doctor/format-default.test.ts # Excluded from src/tools: call-omo-agent/sync-executor.test.ts, call-omo-agent/session-creator.test.ts, session-manager (all) # Excluded from src/hooks/anthropic-context-window-limit-recovery: recovery-hook.test.ts, executor.test.ts + # Build src/shared file list excluding mock-heavy files already run in isolation + SHARED_FILES=$(find src/shared -name '*.test.ts' \ + ! -name 'model-capabilities.test.ts' \ + ! -name 'log-legacy-plugin-startup-warning.test.ts' \ + ! -name 'model-error-classifier.test.ts' \ + ! -name 'opencode-message-dir.test.ts' \ + | sort | tr '\n' ' ') bun test bin script src/config src/mcp src/index.test.ts \ - src/agents src/shared \ + src/agents $SHARED_FILES \ src/cli/run src/cli/config-manager src/cli/mcp-oauth \ src/cli/index.test.ts src/cli/install.test.ts src/cli/model-fallback.test.ts \ src/cli/config-manager.test.ts \ @@ -83,6 +98,7 @@ jobs: src/tools/call-omo-agent/background-executor.test.ts \ src/tools/call-omo-agent/subagent-session-creator.test.ts \ src/hooks/anthropic-context-window-limit-recovery/empty-content-recovery-sdk.test.ts src/hooks/anthropic-context-window-limit-recovery/parser.test.ts src/hooks/anthropic-context-window-limit-recovery/pruning-deduplication.test.ts src/hooks/anthropic-context-window-limit-recovery/recovery-deduplication.test.ts src/hooks/anthropic-context-window-limit-recovery/storage.test.ts \ + src/hooks/session-recovery/detect-error-type.test.ts src/hooks/session-recovery/index.test.ts src/hooks/session-recovery/recover-empty-content-message-sdk.test.ts src/hooks/session-recovery/resume.test.ts src/hooks/session-recovery/storage \ src/hooks/claude-code-compatibility \ src/hooks/context-injection \ src/hooks/provider-toast \ From a2c7fed9d40da0f39ed2ebbd9d516d8d35137f48 Mon Sep 17 00:00:00 2001 From: YeonGyu-Kim Date: Fri, 27 Mar 2026 12:20:40 +0900 Subject: [PATCH 63/63] docs: comprehensive update for v3.14.0 features MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Document object-style fallback_models with per-model settings - Add package rename compatibility layer docs (oh-my-opencode → oh-my-openagent) - Update agent-model-matching with Hephaestus gpt-5.4 default - Document MiniMax M2.5 → M2.7 upgrade across agents - Add agent priority/order deterministic Tab cycling docs - Document file:// URI support for agent prompt field - Add doctor legacy package name warning docs - Update CLI reference with new doctor checks - Document model settings compatibility resolver --- README.md | 20 ++-- docs/guide/agent-model-matching.md | 26 ++++-- docs/guide/installation.md | 82 ++++++++++------- docs/reference/cli.md | 75 ++++++++++----- docs/reference/configuration.md | 141 +++++++++++++++++++++-------- docs/reference/features.md | 72 +++++++++++++-- 6 files changed, 296 insertions(+), 120 deletions(-) diff --git a/README.md b/README.md index 91008474c..b3dcb82df 100644 --- a/README.md +++ b/README.md @@ -111,6 +111,8 @@ Fetch the installation guide and follow it: curl -s https://raw.githubusercontent.com/code-yeongyu/oh-my-openagent/refs/heads/dev/docs/guide/installation.md ``` +**Note**: Use the published package and binary name `oh-my-opencode`. Inside `opencode.json`, the compatibility layer now prefers the plugin entry `oh-my-openagent`, while legacy `oh-my-opencode` entries still load with a warning. Plugin config files still commonly use `oh-my-opencode.json` or `oh-my-opencode.jsonc`, and both legacy and renamed basenames are recognized during the transition. + --- ## Skip This README @@ -273,11 +275,11 @@ To remove oh-my-opencode: 1. **Remove the plugin from your OpenCode config** - Edit `~/.config/opencode/opencode.json` (or `opencode.jsonc`) and remove `"oh-my-opencode"` from the `plugin` array: + Edit `~/.config/opencode/opencode.json` (or `opencode.jsonc`) and remove either `"oh-my-openagent"` or the legacy `"oh-my-opencode"` entry from the `plugin` array: ```bash # Using jq - jq '.plugin = [.plugin[] | select(. != "oh-my-opencode")]' \ + jq '.plugin = [.plugin[] | select(. != "oh-my-openagent" and . != "oh-my-opencode")]' \ ~/.config/opencode/opencode.json > /tmp/oc.json && \ mv /tmp/oc.json ~/.config/opencode/opencode.json ``` @@ -285,11 +287,13 @@ To remove oh-my-opencode: 2. **Remove configuration files (optional)** ```bash - # Remove user config - rm -f ~/.config/opencode/oh-my-opencode.json ~/.config/opencode/oh-my-opencode.jsonc + # Remove plugin config files recognized during the compatibility window + rm -f ~/.config/opencode/oh-my-openagent.jsonc ~/.config/opencode/oh-my-openagent.json \ + ~/.config/opencode/oh-my-opencode.jsonc ~/.config/opencode/oh-my-opencode.json # Remove project config (if exists) - rm -f .opencode/oh-my-opencode.json .opencode/oh-my-opencode.jsonc + rm -f .opencode/oh-my-openagent.jsonc .opencode/oh-my-openagent.json \ + .opencode/oh-my-opencode.jsonc .opencode/oh-my-opencode.json ``` 3. **Verify removal** @@ -315,6 +319,10 @@ See full [Features Documentation](docs/reference/features.md). - **Built-in MCPs**: websearch (Exa), context7 (docs), grep_app (GitHub search) - **Session Tools**: List, read, search, and analyze session history - **Productivity Features**: Ralph Loop, Todo Enforcer, Comment Checker, Think Mode, and more +- **Doctor Command**: Built-in diagnostics (`bunx oh-my-opencode doctor`) verify plugin registration, config, models, and environment +- **Model Fallbacks**: `fallback_models` can mix plain model strings with per-fallback object settings in the same array +- **File Prompts**: Load prompts from files with `file://` support in agent configurations +- **Session Recovery**: Automatic recovery from session errors, context window limits, and API failures - **Model Setup**: Agent-model matching is built into the [Installation Guide](docs/guide/installation.md#step-5-understand-your-model-setup) ## Configuration @@ -324,7 +332,7 @@ Opinionated defaults, adjustable if you insist. See [Configuration Documentation](docs/reference/configuration.md). **Quick Overview:** -- **Config Locations**: `.opencode/oh-my-opencode.jsonc` or `.opencode/oh-my-opencode.json` (project), `~/.config/opencode/oh-my-opencode.jsonc` or `~/.config/opencode/oh-my-opencode.json` (user) +- **Config Locations**: The compatibility layer recognizes both `oh-my-openagent.json[c]` and legacy `oh-my-opencode.json[c]` plugin config files. Existing installs still commonly use the legacy basename. - **JSONC Support**: Comments and trailing commas supported - **Agents**: Override models, temperatures, prompts, and permissions for any agent - **Built-in Skills**: `playwright` (browser automation), `git-master` (atomic commits) diff --git a/docs/guide/agent-model-matching.md b/docs/guide/agent-model-matching.md index ffcbf1b41..6f3f65bef 100644 --- a/docs/guide/agent-model-matching.md +++ b/docs/guide/agent-model-matching.md @@ -92,8 +92,8 @@ These agents do grep, search, and retrieval. They intentionally use the fastest, | Agent | Role | Fallback Chain | Notes | | --------------------- | ------------------ | ---------------------------------------------- | ----------------------------------------------------- | -| **Explore** | Fast codebase grep | Grok Code Fast → opencode-go/minimax-m2.7-highspeed → MiniMax M2.7 → Haiku → GPT-5-Nano | Speed is everything. Fire 10 in parallel. | -| **Librarian** | Docs/code search | opencode-go/minimax-m2.7 → MiniMax M2.7-highspeed → Haiku → GPT-5-Nano | Doc retrieval doesn't need deep reasoning. | +| **Explore** | Fast codebase grep | Grok Code Fast → opencode-go/minimax-m2.7 → opencode/minimax-m2.5 → Haiku → GPT-5-Nano | Speed is everything. Fire 10 in parallel. | +| **Librarian** | Docs/code search | opencode-go/minimax-m2.7 → opencode/minimax-m2.5 → Haiku → GPT-5-Nano | Doc retrieval doesn't need deep reasoning. | | **Multimodal Looker** | Vision/screenshots | GPT-5.4 → opencode-go/kimi-k2.5 → GLM-4.6v → GPT-5-Nano | Uses the first available multimodal-capable fallback. | | **Sisyphus-Junior** | Category executor | Claude Sonnet → opencode-go/kimi-k2.5 → GPT-5.4 → MiniMax M2.7 → Big Pickle | Handles delegated category tasks. Sonnet-tier default. | @@ -131,8 +131,8 @@ Principle-driven, explicit reasoning, deep technical capability. Best for agents | **Gemini 3.1 Pro** | Excels at visual/frontend tasks. Different reasoning style. Default for `visual-engineering` and `artistry`. | | **Gemini 3 Flash** | Fast. Good for doc search and light tasks. | | **Grok Code Fast 1** | Blazing fast code grep. Default for Explore agent. | -| **MiniMax M2.7** | Fast and smart. Good for utility tasks and search/retrieval. Upgraded from M2.5 with better reasoning. | -| **MiniMax M2.7 Highspeed** | Ultra-fast variant. Optimized for latency-sensitive tasks like codebase grep. | +| **MiniMax M2.7** | Fast and smart. Used where provider catalogs expose the newer MiniMax line, especially through OpenCode Go. | +| **MiniMax M2.7 Highspeed** | Ultra-fast variant. You may still see it in older docs, logs, or provider catalogs during the transition. | ### OpenCode Go @@ -144,11 +144,11 @@ A premium subscription tier ($10/month) that provides reliable access to Chinese | ------------------------ | --------------------------------------------------------------------- | | **opencode-go/kimi-k2.5** | Vision-capable, Claude-like reasoning. Used by Sisyphus, Atlas, Sisyphus-Junior, Multimodal Looker. | | **opencode-go/glm-5** | Text-only orchestration model. Used by Oracle, Prometheus, Metis, Momus. | -| **opencode-go/minimax-m2.7** | Ultra-cheap, fast responses. Used by Librarian, Explore, Atlas, Sisyphus-Junior for utility work. | +| **opencode-go/minimax-m2.7** | Ultra-cheap, fast responses. Used by Librarian, Explore, Atlas, and Sisyphus-Junior for utility work. | **When It Gets Used:** -OpenCode Go models appear in fallback chains as intermediate options. They bridge the gap between premium Claude access and free-tier alternatives. The system tries OpenCode Go models before falling back to free tiers (MiniMax M2.7-highspeed, Big Pickle) or GPT alternatives. +OpenCode Go models appear in fallback chains as intermediate options. They bridge the gap between premium Claude access and free-tier alternatives. The system tries OpenCode Go models before falling back to cheaper provider-specific entries like MiniMax or Big Pickle, then GPT alternatives where applicable. **Go-Only Scenarios:** @@ -156,7 +156,7 @@ Some model identifiers like `k2p5` (paid Kimi K2.5) and `glm-5` may only be avai ### About Free-Tier Fallbacks -You may see model names like `kimi-k2.5-free`, `minimax-m2.7-highspeed`, or `big-pickle` (GLM 4.6) in the source code or logs. These are free-tier or speed-optimized versions of the same model families. They exist as lower-priority entries in fallback chains. +You may see model names like `kimi-k2.5-free`, `minimax-m2.7`, `minimax-m2.5`, or `big-pickle` (GLM 4.6) in the source code or logs. These are provider-specific or speed-optimized entries in fallback chains. The exact MiniMax model can differ by provider catalog. You don't need to configure them. The system includes them so it degrades gracefully when you don't have every paid subscription. If you have the paid version, the paid version is always preferred. @@ -187,7 +187,7 @@ See the [Orchestration System Guide](./orchestration.md) for how agents dispatch ```jsonc { - "$schema": "https://raw.githubusercontent.com/code-yeongyu/oh-my-openagent/dev/assets/oh-my-openagent.schema.json", + "$schema": "https://raw.githubusercontent.com/code-yeongyu/oh-my-openagent/dev/assets/oh-my-opencode.schema.json", "agents": { // Main orchestrator: Claude Opus or Kimi K2.5 work best @@ -255,7 +255,15 @@ Run `opencode models` to see available models, `opencode auth login` to authenti ### How Model Resolution Works -Each agent has a fallback chain. The system tries models in priority order until it finds one available through your connected providers. You don't need to configure providers per model — just authenticate (`opencode auth login`) and the system figures out which models are available and where. +Each agent has a fallback chain. The system tries models in priority order until it finds one available through your connected providers. You don't need to configure providers per model. Just authenticate (`opencode auth login`) and the system figures out which models are available and where. + +Core-agent tab cycling is now deterministic. The fixed priority order is Sisyphus, Hephaestus, Prometheus, and Atlas, then the remaining agents follow. + +Your explicit configuration always wins. If you set a specific model for an agent, that choice takes precedence even when resolution data is cold. + +Variant and `reasoningEffort` overrides are normalized to model-supported values, so cross-provider overrides degrade gracefully instead of failing hard. + +To see which models your agents will actually use, run `bunx oh-my-opencode doctor`. This shows effective model resolution based on your current authentication and config. ``` Agent Request → User Override (if configured) → Fallback Chain → System Default diff --git a/docs/guide/installation.md b/docs/guide/installation.md index f90718d90..0b92929cf 100644 --- a/docs/guide/installation.md +++ b/docs/guide/installation.md @@ -5,7 +5,7 @@ Paste this into your llm agent session: ``` -Install and configure oh-my-openagent by following the instructions here: +Install and configure oh-my-opencode by following the instructions here: https://raw.githubusercontent.com/code-yeongyu/oh-my-openagent/refs/heads/dev/docs/guide/installation.md ``` @@ -14,8 +14,8 @@ https://raw.githubusercontent.com/code-yeongyu/oh-my-openagent/refs/heads/dev/do Run the interactive installer: ```bash -bunx oh-my-openagent install # recommended -npx oh-my-openagent install # alternative +bunx oh-my-opencode install # recommended +npx oh-my-opencode install # alternative ``` > **Note**: The CLI ships with standalone binaries for all major platforms. No runtime (Bun/Node.js) is required for CLI execution after installation. @@ -26,6 +26,8 @@ Follow the prompts to configure your Claude, ChatGPT, and Gemini subscriptions. After you install it, you can read this [overview guide](./overview.md) to understand more. +The published package and local binary are still `oh-my-opencode`. Inside `opencode.json`, the compatibility layer now prefers the plugin entry `oh-my-openagent`, while legacy `oh-my-opencode` entries still load with a warning. Plugin config loading recognizes both `oh-my-openagent.json[c]` and `oh-my-opencode.json[c]` during the transition. + ## For LLM Agents > **IMPORTANT: Use `curl` to fetch this file, NOT WebFetch.** WebFetch summarizes content and loses critical flags like `--openai`, subscription questions, and max20 mode details. Always use: @@ -34,7 +36,7 @@ After you install it, you can read this [overview guide](./overview.md) to under > curl -fsSL https://raw.githubusercontent.com/code-yeongyu/oh-my-openagent/refs/heads/dev/docs/guide/installation.md > ``` -If you're an LLM Agent helping set up oh-my-openagent, Welcome! You'll now help your user to install the best agent harness, OhMyOpenAgent. Tell user this with greetings, with test 'oMoMoMoMo...' +If you're an LLM Agent helping set up oh-my-opencode, welcome. You'll help your user install the harness and verify the rename-compat setup cleanly. Tell user this with greetings, with text 'oMoMoMoMo...' follow these steps: @@ -96,19 +98,19 @@ Spawn a subagent to handle installation and report back - to save context. Based on user's answers, run the CLI installer with appropriate flags: ```bash -bunx oh-my-openagent install --no-tui --claude= --gemini= --copilot= [--openai=] [--opencode-go=] [--opencode-zen=] [--zai-coding-plan=] +bunx oh-my-opencode install --no-tui --claude= --gemini= --copilot= [--openai=] [--opencode-go=] [--opencode-zen=] [--zai-coding-plan=] ``` **Examples:** -- User has all native subscriptions: `bunx oh-my-openagent install --no-tui --claude=max20 --openai=yes --gemini=yes --copilot=no` -- User has only Claude: `bunx oh-my-openagent install --no-tui --claude=yes --gemini=no --copilot=no` -- User has Claude + OpenAI: `bunx oh-my-openagent install --no-tui --claude=yes --openai=yes --gemini=no --copilot=no` -- User has only GitHub Copilot: `bunx oh-my-openagent install --no-tui --claude=no --gemini=no --copilot=yes` -- User has Z.ai for Librarian: `bunx oh-my-openagent install --no-tui --claude=yes --gemini=no --copilot=no --zai-coding-plan=yes` -- User has only OpenCode Zen: `bunx oh-my-openagent install --no-tui --claude=no --gemini=no --copilot=no --opencode-zen=yes` -- User has OpenCode Go only: `bunx oh-my-openagent install --no-tui --claude=no --openai=no --gemini=no --copilot=no --opencode-go=yes` -- User has no subscriptions: `bunx oh-my-openagent install --no-tui --claude=no --gemini=no --copilot=no` +- User has all native subscriptions: `bunx oh-my-opencode install --no-tui --claude=max20 --openai=yes --gemini=yes --copilot=no` +- User has only Claude: `bunx oh-my-opencode install --no-tui --claude=yes --gemini=no --copilot=no` +- User has Claude + OpenAI: `bunx oh-my-opencode install --no-tui --claude=yes --openai=yes --gemini=no --copilot=no` +- User has only GitHub Copilot: `bunx oh-my-opencode install --no-tui --claude=no --gemini=no --copilot=yes` +- User has Z.ai for Librarian: `bunx oh-my-opencode install --no-tui --claude=yes --gemini=no --copilot=no --zai-coding-plan=yes` +- User has only OpenCode Zen: `bunx oh-my-opencode install --no-tui --claude=no --gemini=no --copilot=no --opencode-zen=yes` +- User has OpenCode Go only: `bunx oh-my-opencode install --no-tui --claude=no --openai=no --gemini=no --copilot=no --opencode-go=yes` +- User has no subscriptions: `bunx oh-my-opencode install --no-tui --claude=no --gemini=no --copilot=no` The CLI will: @@ -120,8 +122,17 @@ The CLI will: ```bash opencode --version # Should be 1.0.150 or higher -cat ~/.config/opencode/opencode.json # Should contain "oh-my-openagent" in plugin array +cat ~/.config/opencode/opencode.json # Should contain "oh-my-openagent" in plugin array, or the legacy "oh-my-opencode" entry while you are still migrating ``` +#### Run Doctor Verification + +After installation, verify everything is working correctly: + +```bash +bunx oh-my-opencode doctor +``` + +This checks your environment, authentication status, and shows which models each agent will actually use. ### Step 4: Configure Authentication @@ -154,9 +165,9 @@ First, add the opencode-antigravity-auth plugin: You'll also need full model settings in `opencode.json`. Read the [opencode-antigravity-auth documentation](https://github.com/NoeFabris/opencode-antigravity-auth), copy the full model configuration from the README, and merge carefully to avoid breaking the user's existing setup. The plugin now uses a **variant system** — models like `antigravity-gemini-3-pro` support `low`/`high` variants instead of separate `-low`/`-high` model entries. -##### oh-my-openagent Agent Model Override +##### Plugin config model override -The `opencode-antigravity-auth` plugin uses different model names than the built-in Google auth. Override the agent models in `oh-my-openagent.json` (or `.opencode/oh-my-openagent.json`): +The `opencode-antigravity-auth` plugin uses different model names than the built-in Google auth. Override the agent models in your plugin config file. Existing installs still commonly use `oh-my-opencode.json` or `.opencode/oh-my-opencode.json`, while the compatibility layer also recognizes `oh-my-openagent.json[c]`. ```json { @@ -201,7 +212,7 @@ GitHub Copilot is supported as a **fallback provider** when native providers are ##### Model Mappings -When GitHub Copilot is the best available provider, oh-my-openagent uses these model assignments: +When GitHub Copilot is the best available provider, the compatibility layer resolves these assignments: | Agent | Model | | ------------- | --------------------------------- | @@ -227,23 +238,22 @@ If Z.ai is your main provider, the most important fallbacks are: #### OpenCode Zen -OpenCode Zen provides access to `opencode/` prefixed models including `opencode/claude-opus-4-6`, `opencode/gpt-5.4`, `opencode/gpt-5.3-codex`, `opencode/gpt-5-nano`, `opencode/glm-5`, `opencode/big-pickle`, and `opencode/minimax-m2.7-highspeed`. +OpenCode Zen provides access to `opencode/` prefixed models including `opencode/claude-opus-4-6`, `opencode/gpt-5.4`, `opencode/gpt-5.3-codex`, `opencode/gpt-5-nano`, `opencode/glm-5`, `opencode/big-pickle`, and `opencode/minimax-m2.5`. -When OpenCode Zen is the best available provider (no native or Copilot), these models are used: +When OpenCode Zen is the best available provider, these are the most relevant source-backed examples: | Agent | Model | | ------------- | ---------------------------------------------------- | | **Sisyphus** | `opencode/claude-opus-4-6` | | **Oracle** | `opencode/gpt-5.4` | -| **Explore** | `opencode/gpt-5-nano` | -| **Librarian** | `opencode/minimax-m2.7-highspeed` / `opencode/big-pickle` | +| **Explore** | `opencode/claude-haiku-4-5` | ##### Setup Run the installer and select "Yes" for GitHub Copilot: ```bash -bunx oh-my-openagent install +bunx oh-my-opencode install # Select your subscriptions (Claude, ChatGPT, Gemini) # When prompted: "Do you have a GitHub Copilot subscription?" → Select "Yes" ``` @@ -251,7 +261,7 @@ bunx oh-my-openagent install Or use non-interactive mode: ```bash -bunx oh-my-openagent install --no-tui --claude=no --openai=no --gemini=no --copilot=yes +bunx oh-my-opencode install --no-tui --claude=no --openai=no --gemini=no --copilot=yes ``` Then authenticate with GitHub: @@ -263,7 +273,7 @@ opencode auth login ### Step 5: Understand Your Model Setup -You've just configured oh-my-openagent. Here's what got set up and why. +You've just configured oh-my-opencode. Here's what got set up and why. #### Model Families: What You're Working With @@ -296,8 +306,8 @@ Not all models behave the same way. Understanding which models are "similar" hel | --------------------- | -------------------------------- | ----------------------------------------------------------- | | **Gemini 3.1 Pro** | google, github-copilot, opencode | Excels at visual/frontend tasks. Different reasoning style. | | **Gemini 3 Flash** | google, github-copilot, opencode | Fast, good for doc search and light tasks. | -| **MiniMax M2.7** | venice, opencode-go | Fast and smart. Good for utility tasks. Upgraded from M2.5. | -| **MiniMax M2.7 Highspeed** | opencode | Ultra-fast MiniMax variant. Optimized for latency. | +| **MiniMax M2.7** | venice, opencode-go | Fast and smart. Good for utility tasks where the provider catalog exposes M2.7. | +| **MiniMax M2.5** | opencode | Legacy OpenCode catalog entry still used in some fallback chains for compatibility. | **Speed-Focused Models**: @@ -305,7 +315,7 @@ Not all models behave the same way. Understanding which models are "similar" hel | ----------------------- | ---------------------- | -------------- | --------------------------------------------------------------------------------------------------------------------------------------------- | | **Grok Code Fast 1** | github-copilot, venice | Very fast | Optimized for code grep/search. Default for Explore. | | **Claude Haiku 4.5** | anthropic, opencode | Fast | Good balance of speed and intelligence. | -| **MiniMax M2.7 Highspeed** | opencode | Very fast | Ultra-fast MiniMax variant. Smart for its speed class. | +| **MiniMax M2.5** | opencode | Very fast | Legacy OpenCode catalog entry that still appears in some utility fallback chains. | | **GPT-5.3-codex-spark** | openai | Extremely fast | Blazing fast but compacts so aggressively that oh-my-openagent's context management doesn't work well with it. Not recommended for omo agents. | #### What Each Agent Does and Which Model It Got @@ -316,7 +326,7 @@ Based on your subscriptions, here's how the agents were configured: | Agent | Role | Default Chain | What It Does | | ------------ | ---------------- | ----------------------------------------------- | ---------------------------------------------------------------------------------------- | -| **Sisyphus** | Main ultraworker | Opus (max) → Kimi K2.5 → GLM 5 → Big Pickle | Primary coding agent. Orchestrates everything. **Never use GPT — no GPT prompt exists.** | +| **Sisyphus** | Main ultraworker | Opus (max) → Kimi K2.5 → GPT-5.4 → GLM 5 → Big Pickle | Primary coding agent. Orchestrates everything. Claude-family models are still preferred, but GPT-5.4 now has a dedicated prompt path. | | **Metis** | Plan review | Opus (max) → Kimi K2.5 → GPT-5.4 → Gemini 3.1 Pro | Reviews Prometheus plans for gaps. | **Dual-Prompt Agents** (auto-switch between Claude and GPT prompts): @@ -328,7 +338,7 @@ Priority: **Claude > GPT > Claude-like models** | Agent | Role | Default Chain | GPT Prompt? | | -------------- | ----------------- | ---------------------------------------------------------- | ---------------------------------------------------------------- | | **Prometheus** | Strategic planner | Opus (max) → **GPT-5.4 (high)** → Kimi K2.5 → Gemini 3.1 Pro | Yes — XML-tagged, principle-driven (~300 lines vs ~1,100 Claude) | -| **Atlas** | Todo orchestrator | **Kimi K2.5** → Sonnet → GPT-5.4 | Yes — GPT-optimized todo management | +| **Atlas** | Todo orchestrator | **Claude Sonnet 4.6** → Kimi K2.5 → GPT-5.4 | Yes - GPT-optimized todo management | **GPT-Native Agents** (built for GPT, don't override to Claude): @@ -344,9 +354,9 @@ These agents do search, grep, and retrieval. They intentionally use fast, cheap | Agent | Role | Default Chain | Design Rationale | | --------------------- | ------------------ | ---------------------------------------------------------------------- | -------------------------------------------------------------- | -| **Explore** | Fast codebase grep | Grok Code Fast → MiniMax M2.7-highspeed → MiniMax M2.7 → Haiku → GPT-5-Nano | Speed is everything. Grok is blazing fast for grep. | -| **Librarian** | Docs/code search | MiniMax M2.7 → MiniMax M2.7-highspeed → Haiku → GPT-5-Nano | Doc retrieval doesn't need deep reasoning. MiniMax is fast. | -| **Multimodal Looker** | Vision/screenshots | Kimi K2.5 → Kimi Free → Gemini Flash → GPT-5.4 → GLM-4.6v | Kimi excels at multimodal understanding. | +| **Explore** | Fast codebase grep | Grok Code Fast → OpenCode Go MiniMax M2.7 → OpenCode MiniMax M2.5 → Haiku → GPT-5-Nano | Speed is everything. Grok is blazing fast for grep. | +| **Librarian** | Docs/code search | OpenCode Go MiniMax M2.7 → OpenCode MiniMax M2.5 → Haiku → GPT-5-Nano | Doc retrieval doesn't need deep reasoning. MiniMax is fast where the provider catalog supports it. | +| **Multimodal Looker** | Vision/screenshots | GPT-5.4 → Kimi K2.5 → GLM-4.6v → GPT-5-Nano | GPT-5.4 now leads the default vision path when available. | #### Why Different Models Need Different Prompts @@ -365,7 +375,7 @@ This is why Prometheus and Atlas ship separate prompts per model family — they #### Custom Model Configuration -If the user wants to override which model an agent uses, you can customize in `oh-my-openagent.json`: +If the user wants to override which model an agent uses, you can customize in your plugin config file. Existing installs still commonly use `oh-my-opencode.json`, while the compatibility layer also recognizes `oh-my-openagent.json[c]`. ```jsonc { @@ -400,7 +410,7 @@ GPT (5.3-codex, 5.2) > Claude Opus (decent fallback) > Gemini (acceptable) **Dangerous** (no prompt support): -- Sisyphus → GPT: **No GPT prompt. Will degrade significantly.** +- Sisyphus → older GPT models: **Still a bad fit. GPT-5.4 is the only dedicated GPT prompt path.** - Hephaestus → Claude: **Built for Codex. Claude can't replicate this.** - Explore → Opus: **Massive cost waste. Explore needs speed, not intelligence.** - Librarian → Opus: **Same. Doc search doesn't need Opus-level reasoning.** @@ -462,3 +472,7 @@ Tell the user of following: 4. You wanna have your own agent- catalog setup? I can read the [docs](docs/guide/agent-model-matching.md) and set up for you after interviewing! That's it. The agent will figure out the rest and handle everything automatically. + +#### Advanced Configuration + +You can customize agent models and fallback chains in your config. The `fallback_models` field accepts either a single string or an array that mixes strings and per-model objects with settings like `variant` and `temperature`. See the [Configuration Reference](../reference/configuration.md) and example configs in `docs/examples/` for details. diff --git a/docs/reference/cli.md b/docs/reference/cli.md index 2d47a59ce..e467ae526 100644 --- a/docs/reference/cli.md +++ b/docs/reference/cli.md @@ -1,15 +1,15 @@ # CLI Reference -Complete reference for the `oh-my-openagent` command-line interface. +Complete reference for the published `oh-my-opencode` CLI. During the rename transition, OpenCode plugin registration now prefers `oh-my-openagent` inside `opencode.json`. ## Basic Usage ```bash # Display help -bunx oh-my-openagent +bunx oh-my-opencode # Or with npx -npx oh-my-openagent +npx oh-my-opencode ``` ## Commands @@ -27,20 +27,20 @@ npx oh-my-openagent ## install -Interactive installation tool for initial Oh-My-OpenAgent setup. Provides a TUI based on `@clack/prompts`. +Interactive installation tool for initial Oh My OpenCode setup. Provides a TUI based on `@clack/prompts`. ### Usage ```bash -bunx oh-my-openagent install +bunx oh-my-opencode install ``` ### Installation Process 1. **Provider Selection**: Choose your AI provider (Claude, ChatGPT, or Gemini) 2. **API Key Input**: Enter the API key for your selected provider -3. **Configuration File Creation**: Generates `opencode.json` or `oh-my-openagent.json` files -4. **Plugin Registration**: Automatically registers the oh-my-openagent plugin in OpenCode settings +3. **Configuration File Creation**: Writes the plugin config file used by the current install path. Existing installs still commonly use `oh-my-opencode.json`, while renamed `oh-my-openagent.json[c]` files are also recognized. +4. **Plugin Registration**: Registers `oh-my-openagent` in OpenCode settings, or upgrades a legacy `oh-my-opencode` entry during the compatibility window ### Options @@ -53,12 +53,18 @@ bunx oh-my-openagent install ## doctor -Diagnoses your environment to ensure Oh-My-OpenAgent is functioning correctly. Performs 17+ health checks. +Diagnoses your environment to ensure Oh My OpenCode is functioning correctly. Performs 17+ health checks covering installation, configuration, authentication, dependencies, and tools. +The doctor command detects common issues including: +- Legacy plugin entry references in `opencode.json` (warns when `oh-my-opencode` is still used instead of `oh-my-openagent`) +- Configuration file validity and JSONC parsing errors +- Model resolution and fallback chain verification +- API key validity for configured providers +- Missing or misconfigured MCP servers ### Usage ```bash -bunx oh-my-openagent doctor +bunx oh-my-opencode doctor ``` ### Diagnostic Categories @@ -83,7 +89,7 @@ bunx oh-my-openagent doctor ### Example Output ``` -oh-my-openagent doctor +oh-my-opencode doctor ┌──────────────────────────────────────────────────┐ │ Oh-My-OpenAgent Doctor │ @@ -94,7 +100,8 @@ Installation ✓ Plugin registered in opencode.json Configuration - ✓ oh-my-openagent.json is valid + ✓ oh-my-opencode.jsonc is valid + ✓ Model resolution: all agents have valid fallback chains ⚠ categories.visual-engineering: using default model Authentication @@ -109,7 +116,6 @@ Dependencies Summary: 10 passed, 1 warning, 1 failed ``` - --- ## run @@ -119,7 +125,7 @@ Executes OpenCode sessions and monitors task completion. ### Usage ```bash -bunx oh-my-openagent run [prompt] +bunx oh-my-opencode run [prompt] ``` ### Options @@ -148,16 +154,16 @@ Manages OAuth 2.1 authentication for remote MCP servers. ```bash # Login to an OAuth-protected MCP server -bunx oh-my-openagent mcp oauth login --server-url https://api.example.com +bunx oh-my-opencode mcp oauth login --server-url https://api.example.com # Login with explicit client ID and scopes -bunx oh-my-openagent mcp oauth login my-api --server-url https://api.example.com --client-id my-client --scopes "read,write" +bunx oh-my-opencode mcp oauth login my-api --server-url https://api.example.com --client-id my-client --scopes "read,write" # Remove stored OAuth tokens -bunx oh-my-openagent mcp oauth logout +bunx oh-my-opencode mcp oauth logout # Check OAuth token status -bunx oh-my-openagent mcp oauth status [server-name] +bunx oh-my-opencode mcp oauth status [server-name] ``` ### Options @@ -178,8 +184,18 @@ Tokens are stored in `~/.config/opencode/mcp-oauth.json` with `0600` permissions The CLI searches for configuration files in the following locations (in priority order): -1. **Project Level**: `.opencode/oh-my-openagent.json` -2. **User Level**: `~/.config/opencode/oh-my-openagent.json` +1. **Project Level**: `.opencode/oh-my-openagent.jsonc`, `.opencode/oh-my-openagent.json`, `.opencode/oh-my-opencode.jsonc`, or `.opencode/oh-my-opencode.json` +2. **User Level**: `~/.config/opencode/oh-my-openagent.jsonc`, `~/.config/opencode/oh-my-openagent.json`, `~/.config/opencode/oh-my-opencode.jsonc`, or `~/.config/opencode/oh-my-opencode.json` + +**Naming Note**: The published package and binary are still `oh-my-opencode`. Inside `opencode.json`, the compatibility layer now prefers the plugin entry `oh-my-openagent`. Plugin config loading recognizes both `oh-my-openagent.*` and legacy `oh-my-opencode.*` basenames. If both basenames exist in the same directory, the legacy `oh-my-opencode.*` file currently wins. + +### Filename Compatibility + +Both `.jsonc` and `.json` extensions are supported. JSONC (JSON with Comments) is preferred as it allows: +- Comments (both `//` and `/* */` styles) +- Trailing commas in arrays and objects + +If both `.jsonc` and `.json` exist in the same directory, the `.jsonc` file takes precedence. ### JSONC Support @@ -219,31 +235,40 @@ bun install -g opencode@latest ```bash # Reinstall plugin -bunx oh-my-openagent install +bunx oh-my-opencode install ``` ### Doctor Check Failures ```bash # Diagnose with detailed information -bunx oh-my-openagent doctor --verbose +bunx oh-my-opencode doctor --verbose # Check specific category only -bunx oh-my-openagent doctor --category authentication +bunx oh-my-opencode doctor --category authentication ``` +### "Using legacy package name" Warning + +The doctor warns if it finds the legacy plugin entry `oh-my-opencode` in `opencode.json`. Update the plugin array to the canonical `oh-my-openagent` entry: + +```bash +# Replace the legacy plugin entry in user config +jq '.plugin = (.plugin // [] | map(if . == "oh-my-opencode" then "oh-my-openagent" else . end))' \ + ~/.config/opencode/opencode.json > /tmp/opencode.json && mv /tmp/opencode.json ~/.config/opencode/opencode.json +``` --- ## Non-Interactive Mode -Use the `--no-tui` option for CI/CD environments. +Use JSON output for CI or scripted diagnostics. ```bash # Run doctor in CI environment -bunx oh-my-openagent doctor --no-tui --json +bunx oh-my-opencode doctor --json # Save results to file -bunx oh-my-openagent doctor --json > doctor-report.json +bunx oh-my-opencode doctor --json > doctor-report.json ``` --- diff --git a/docs/reference/configuration.md b/docs/reference/configuration.md index 465360892..41dad6d34 100644 --- a/docs/reference/configuration.md +++ b/docs/reference/configuration.md @@ -1,6 +1,6 @@ # Configuration Reference -Complete reference for `oh-my-openagent.jsonc` configuration. This document covers every available option with examples. +Complete reference for Oh My OpenCode plugin configuration. During the rename transition, the runtime recognizes both `oh-my-openagent.json[c]` and legacy `oh-my-opencode.json[c]` files. --- @@ -42,27 +42,28 @@ Complete reference for `oh-my-openagent.jsonc` configuration. This document cove ### File Locations -Priority order (project overrides user): +User config is loaded first, then project config overrides it. In each directory, the compatibility layer recognizes both the renamed and legacy basenames. -1. `.opencode/oh-my-openagent.jsonc` / `.opencode/oh-my-openagent.json` +1. Project config: `.opencode/oh-my-openagent.json[c]` or `.opencode/oh-my-opencode.json[c]` 2. User config (`.jsonc` preferred over `.json`): -| Platform | Path | -| ----------- | ----------------------------------------- | -| macOS/Linux | `~/.config/opencode/oh-my-openagent.jsonc` | -| Windows | `%APPDATA%\opencode\oh-my-openagent.jsonc` | +| Platform | Path candidates | +| ----------- | --------------- | +| macOS/Linux | `~/.config/opencode/oh-my-openagent.json[c]`, `~/.config/opencode/oh-my-opencode.json[c]` | +| Windows | `%APPDATA%\opencode\oh-my-openagent.json[c]`, `%APPDATA%\opencode\oh-my-opencode.json[c]` | +**Rename compatibility:** OpenCode plugin registration now prefers `oh-my-openagent`, while legacy `oh-my-opencode` entries and config basenames still load during the transition. If both plugin config basenames exist in the same directory, the legacy `oh-my-opencode.*` file currently wins. JSONC supports `// line comments`, `/* block comments */`, and trailing commas. Enable schema autocomplete: ```json { - "$schema": "https://raw.githubusercontent.com/code-yeongyu/oh-my-openagent/dev/assets/oh-my-openagent.schema.json" + "$schema": "https://raw.githubusercontent.com/code-yeongyu/oh-my-openagent/dev/assets/oh-my-opencode.schema.json" } ``` -Run `bunx oh-my-openagent install` for guided setup. Run `opencode models` to list available models. +Run `bunx oh-my-opencode install` for guided setup. Run `opencode models` to list available models. ### Quick Start Example @@ -70,7 +71,7 @@ Here's a practical starting configuration: ```jsonc { - "$schema": "https://raw.githubusercontent.com/code-yeongyu/oh-my-openagent/dev/assets/oh-my-openagent.schema.json", + "$schema": "https://raw.githubusercontent.com/code-yeongyu/oh-my-openagent/dev/assets/oh-my-opencode.schema.json", "agents": { // Main orchestrator: Claude Opus or Kimi K2.5 work best @@ -93,19 +94,19 @@ Here's a practical starting configuration: }, "categories": { - // quick — trivial tasks + // quick - trivial tasks "quick": { "model": "opencode/gpt-5-nano" }, - // unspecified-low — moderate tasks + // unspecified-low - moderate tasks "unspecified-low": { "model": "anthropic/claude-sonnet-4-6" }, - // unspecified-high — complex work + // unspecified-high - complex work "unspecified-high": { "model": "anthropic/claude-opus-4-6", "variant": "max" }, - // writing — docs/prose + // writing - docs/prose "writing": { "model": "google/gemini-3-flash" }, - // visual-engineering — Gemini dominates visual tasks + // visual-engineering - Gemini dominates visual tasks "visual-engineering": { "model": "google/gemini-3.1-pro", "variant": "high", @@ -159,24 +160,24 @@ Disable agents entirely: `{ "disabled_agents": ["oracle", "multimodal-looker"] } #### Agent Options -| Option | Type | Description | -| ----------------- | ------------- | ------------------------------------------------------ | -| `model` | string | Model override (`provider/model`) | -| `fallback_models` | string\|array | Fallback models on API errors | -| `temperature` | number | Sampling temperature | -| `top_p` | number | Top-p sampling | -| `prompt` | string | Replace system prompt | -| `prompt_append` | string | Append to system prompt | +| Option | Type | Description | +| ----------------- | -------------- | --------------------------------------------------------------- | +| `model` | string | Model override (`provider/model`) | +| `fallback_models` | string\|array | Fallback models on API errors. Arrays can mix plain strings and per-model objects | +| `temperature` | number | Sampling temperature | +| `top_p` | number | Top-p sampling | +| `prompt` | string | Replace system prompt. Supports `file://` URIs | +| `prompt_append` | string | Append to system prompt. Supports `file://` URIs | | `tools` | array | Allowed tools list | | `disable` | boolean | Disable this agent | | `mode` | string | Agent mode | | `color` | string | UI color | | `permission` | object | Per-tool permissions (see below) | | `category` | string | Inherit model from category | -| `variant` | string | Model variant: `max`, `high`, `medium`, `low`, `xhigh` | +| `variant` | string | Model variant: `max`, `high`, `medium`, `low`, `xhigh`. Normalized to supported values | | `maxTokens` | number | Max response tokens | | `thinking` | object | Anthropic extended thinking | -| `reasoningEffort` | string | OpenAI reasoning: `low`, `medium`, `high`, `xhigh` | +| `reasoningEffort` | string | OpenAI reasoning: `low`, `medium`, `high`, `xhigh`. Normalized to supported values | | `textVerbosity` | string | Text verbosity: `low`, `medium`, `high` | | `providerOptions` | object | Provider-specific options | @@ -216,6 +217,58 @@ Control what tools an agent can use: | `doom_loop` | `ask` / `allow` / `deny` | | `external_directory` | `ask` / `allow` / `deny` | + +#### Fallback Models with Per-Model Settings + +`fallback_models` accepts either a single model string or an array. Array entries can be plain strings or objects with individual model settings: + +```jsonc +{ + "agents": { + "sisyphus": { + "model": "anthropic/claude-opus-4-6", + "fallback_models": [ + // Simple string fallback + "openai/gpt-5.4", + // Object with per-model settings + { + "model": "google/gemini-3.1-pro", + "variant": "high", + "temperature": 0.2 + }, + { + "model": "anthropic/claude-sonnet-4-6", + "thinking": { "type": "enabled", "budgetTokens": 64000 } + } + ] + } + } +} +``` + +Object entries support: `model`, `variant`, `reasoningEffort`, `temperature`, `top_p`, `maxTokens`, `thinking`. + +#### File URIs for Prompts + +Both `prompt` and `prompt_append` support loading content from files via `file://` URIs: + +```jsonc +{ + "agents": { + "sisyphus": { + "prompt_append": "file:///absolute/path/to/prompt.txt" + }, + "oracle": { + "prompt": "file://./relative/to/project/prompt.md" + }, + "explore": { + "prompt_append": "file://~/home/dir/prompt.txt" + } + } +} +``` + +Paths can be absolute (`file:///abs/path`), relative to project root (`file://./rel/path`), or home-relative (`file://~/home/path`). ### Categories Domain-specific model delegation used by the `task()` tool. When Sisyphus delegates work, it picks a category, not a model name. @@ -240,16 +293,16 @@ Domain-specific model delegation used by the `task()` tool. When Sisyphus delega | Option | Type | Default | Description | | ------------------- | ------------- | ------- | ------------------------------------------------------------------- | | `model` | string | - | Model override | -| `fallback_models` | string\|array | - | Fallback models on API errors | +| `fallback_models` | string\|array | - | Fallback models on API errors. Arrays can mix plain strings and per-model objects | | `temperature` | number | - | Sampling temperature | | `top_p` | number | - | Top-p sampling | | `maxTokens` | number | - | Max response tokens | | `thinking` | object | - | Anthropic extended thinking | -| `reasoningEffort` | string | - | OpenAI reasoning effort | +| `reasoningEffort` | string | - | OpenAI reasoning effort. Unsupported values are normalized | | `textVerbosity` | string | - | Text verbosity | | `tools` | array | - | Allowed tools | | `prompt_append` | string | - | Append to system prompt | -| `variant` | string | - | Model variant | +| `variant` | string | - | Model variant. Unsupported values are normalized | | `description` | string | - | Shown in `task()` tool prompt | | `is_unstable_agent` | boolean | `false` | Force background mode + monitoring. Auto-enabled for Gemini models. | @@ -259,9 +312,20 @@ Disable categories: `{ "disabled_categories": ["ultrabrain"] }` 3-step priority at runtime: -1. **User override** — model set in config → used exactly as-is -2. **Provider fallback chain** — tries each provider in priority order until available -3. **System default** — falls back to OpenCode's configured default model +1. **User override** - model set in config → used exactly as-is. Even on cold cache (first run without model availability data), explicit user configuration takes precedence over hardcoded fallback chains +2. **Provider fallback chain** - tries each provider in priority order until available +3. **System default** - falls back to OpenCode's configured default model + +#### Model Settings Compatibility + +`variant` and `reasoningEffort` values are automatically normalized to what each model supports. If you specify a variant or reasoning effort level that a model does not support, it is adjusted to the closest supported value rather than causing errors. + +Examples: +- Claude models do not support `reasoningEffort` - it is removed automatically +- GPT-4.1 does not support reasoning - `reasoningEffort` is removed +- o-series models support `none` through `high` - `xhigh` is downgraded to `high` +- GPT-5 supports `none`, `minimal`, `low`, `medium`, `high`, `xhigh` - all pass through + #### Agent Provider Chains @@ -270,9 +334,9 @@ Disable categories: `{ "disabled_categories": ["ultrabrain"] }` | **Sisyphus** | `claude-opus-4-6` | `claude-opus-4-6` → `glm-5` → `big-pickle` | | **Hephaestus** | `gpt-5.4` | `gpt-5.4` | | **oracle** | `gpt-5.4` | `gpt-5.4` → `gemini-3.1-pro` → `claude-opus-4-6` | -| **librarian** | `minimax-m2.7` | `minimax-m2.7` → `minimax-m2.7-highspeed` → `claude-haiku-4-5` → `gpt-5-nano` | -| **explore** | `grok-code-fast-1` | `grok-code-fast-1` → `minimax-m2.7-highspeed` → `minimax-m2.7` → `claude-haiku-4-5` → `gpt-5-nano` | -| **multimodal-looker** | `gpt-5.3-codex` | `gpt-5.3-codex` → `k2p5` → `gemini-3-flash` → `glm-4.6v` → `gpt-5-nano` | +| **librarian** | `minimax-m2.7` | `opencode-go/minimax-m2.7` → `opencode/minimax-m2.5` → `claude-haiku-4-5` → `gpt-5-nano` | +| **explore** | `grok-code-fast-1` | `grok-code-fast-1` → `opencode-go/minimax-m2.7` → `opencode/minimax-m2.5` → `claude-haiku-4-5` → `gpt-5-nano` | +| **multimodal-looker** | `gpt-5.4` | `gpt-5.4` → `k2p5` → `glm-4.6v` → `gpt-5-nano` | | **Prometheus** | `claude-opus-4-6` | `claude-opus-4-6` → `gpt-5.4` → `gemini-3.1-pro` | | **Metis** | `claude-opus-4-6` | `claude-opus-4-6` → `gpt-5.4` → `gemini-3.1-pro` | | **Momus** | `gpt-5.4` | `gpt-5.4` → `claude-opus-4-6` → `gemini-3.1-pro` | @@ -291,7 +355,7 @@ Disable categories: `{ "disabled_categories": ["ultrabrain"] }` | **unspecified-high** | `claude-opus-4-6` | `claude-opus-4-6` → `gpt-5.4 (high)` → `glm-5` → `k2p5` → `kimi-k2.5` | | **writing** | `gemini-3-flash` | `gemini-3-flash` → `claude-sonnet-4-6` → `minimax-m2.7` | -Run `bunx oh-my-openagent doctor --verbose` to see effective model resolution for your config. +Run `bunx oh-my-opencode doctor --verbose` to see effective model resolution for your config. --- @@ -425,9 +489,10 @@ Available hooks: `todo-continuation-enforcer`, `context-window-monitor`, `sessio **Notes:** -- `directory-agents-injector` — auto-disabled on OpenCode 1.1.37+ (native AGENTS.md support) -- `no-sisyphus-gpt` — **do not disable**. It blocks incompatible GPT models for Sisyphus while allowing the dedicated GPT-5.4 prompt path. +- `directory-agents-injector` - auto-disabled on OpenCode 1.1.37+ (native AGENTS.md support) +- `no-sisyphus-gpt` - **do not disable**. It blocks incompatible GPT models for Sisyphus while allowing the dedicated GPT-5.4 prompt path. - `startup-toast` is a sub-feature of `auto-update-checker`. Disable just the toast by adding `startup-toast` to `disabled_hooks`. +- `session-recovery` - automatically recovers from recoverable session errors (missing tool results, unavailable tools, thinking block violations). Shows toast notifications during recovery. Enable `experimental.auto_resume` for automatic retry after recovery. ### Commands @@ -504,7 +569,7 @@ Force-enable session notifications: { "notification": { "force_enable": true } } ``` -`force_enable` (`false`) — force session-notification even if external notification plugins are detected. +`force_enable` (`false`) - force session-notification even if external notification plugins are detected. ### MCPs diff --git a/docs/reference/features.md b/docs/reference/features.md index 6dbb9833f..6d1043ce5 100644 --- a/docs/reference/features.md +++ b/docs/reference/features.md @@ -6,15 +6,16 @@ Oh-My-OpenAgent provides 11 specialized AI agents. Each has distinct expertise, ### Core Agents +Core-agent tab cycling is deterministic. The fixed priority order is Sisyphus, Hephaestus, Prometheus, and Atlas. Remaining agents follow after that stable core ordering. + | Agent | Model | Purpose | | --------------------- | ------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| **Sisyphus** | `claude-opus-4-6` | The default orchestrator. Plans, delegates, and executes complex tasks using specialized subagents with aggressive parallel execution. Todo-driven workflow with extended thinking (32k budget). Fallback: `glm-5` → `big-pickle`. | +| **Sisyphus** | `claude-opus-4-6` | The default orchestrator. Plans, delegates, and executes complex tasks using specialized subagents with aggressive parallel execution. Todo-driven workflow with extended thinking (32k budget). Fallback: `glm-5` → `big-pickle`. | | **Hephaestus** | `gpt-5.4` | The Legitimate Craftsman. Autonomous deep worker inspired by AmpCode's deep mode. Goal-oriented execution with thorough research before action. Explores codebase patterns, completes tasks end-to-end without premature stopping. Named after the Greek god of forge and craftsmanship. Requires a GPT-capable provider. | | **Oracle** | `gpt-5.4` | Architecture decisions, code review, debugging. Read-only consultation with stellar logical reasoning and deep analysis. Inspired by AmpCode. Fallback: `gemini-3.1-pro` → `claude-opus-4-6`. | -| **Librarian** | `minimax-m2.7` | Multi-repo analysis, documentation lookup, OSS implementation examples. Deep codebase understanding with evidence-based answers. Fallback: `minimax-m2.7-highspeed` → `claude-haiku-4-5` → `gpt-5-nano`. | -| **Explore** | `grok-code-fast-1` | Fast codebase exploration and contextual grep. Fallback: `minimax-m2.7-highspeed` → `minimax-m2.7` → `claude-haiku-4-5` → `gpt-5-nano`. | -| **Multimodal-Looker** | `gpt-5.3-codex` | Visual content specialist. Analyzes PDFs, images, diagrams to extract information. Fallback: `k2p5` → `gemini-3-flash` → `glm-4.6v` → `gpt-5-nano`. | - +| **Librarian** | `minimax-m2.7` | Multi-repo analysis, documentation lookup, OSS implementation examples. Deep codebase understanding with evidence-based answers. Primary OpenCode Go path uses MiniMax M2.7. Other provider catalogs may still fall back to MiniMax M2.5, then `claude-haiku-4-5` and `gpt-5-nano`. | +| **Explore** | `grok-code-fast-1` | Fast codebase exploration and contextual grep. Primary path stays on Grok Code Fast 1. MiniMax M2.7 is now used where provider catalogs expose it, while some OpenCode fallback paths still use MiniMax M2.5 for catalog compatibility. | +| **Multimodal-Looker** | `gpt-5.4` | Visual content specialist. Analyzes PDFs, images, diagrams to extract information. Fallback: `k2p5` → `glm-4.6v` → `gpt-5-nano`. | ### Planning Agents | Agent | Model | Purpose | @@ -89,8 +90,9 @@ When running inside tmux: - Watch multiple agents work in real-time - Each pane shows agent output live - Auto-cleanup when agents complete +- **Stable agent ordering**: the core tab cycle stays deterministic with Sisyphus, Hephaestus, Prometheus, and Atlas first -Customize agent models, prompts, and permissions in `oh-my-openagent.json`. +Customize agent models, prompts, and permissions in `oh-my-opencode.jsonc`. ## Category System @@ -129,7 +131,7 @@ task({ ### Custom Categories -You can define custom categories in `oh-my-openagent.json`. +You can define custom categories in your plugin config file. During the rename transition, both `oh-my-openagent.json[c]` and legacy `oh-my-opencode.json[c]` basenames are recognized. #### Category Configuration Schema @@ -188,6 +190,60 @@ When you use a Category, a special agent called **Sisyphus-Junior** performs the - **Characteristic**: Cannot **re-delegate** tasks to other agents. - **Purpose**: Prevents infinite delegation loops and ensures focus on the assigned task. +## Advanced Configuration + +### Fallback Models + +Configure per-agent fallback chains with arrays that can mix plain model strings and per-model objects: + +```jsonc +{ + "agents": { + "sisyphus": { + "fallback_models": [ + "opencode/glm-5", + { "model": "openai/gpt-5.4", "variant": "high" }, + { "model": "anthropic/claude-sonnet-4-6", "thinking": { "type": "enabled", "budgetTokens": 64000 } } + ] + } + } +} +``` + +When a model errors, the runtime can move through the configured fallback array. Object entries let you tune the backup model itself instead of only swapping the model name. + +### File-Based Prompts + +Load agent system prompts from external files using `file://` URLs: + +```jsonc +{ + "agents": { + "sisyphus": { + "prompt": "file:///path/to/custom-prompt.md" + } + } +} +``` + +Useful for: +- Version controlling prompts separately from config +- Sharing prompts across projects +- Keeping configuration files concise + +The file content is loaded at runtime and injected as the agent's system prompt. + +### Session Recovery + +The system automatically recovers from common session failures without user intervention: + +- **Missing tool results**: reconstructs recoverable tool state and skips invalid tool-part IDs instead of failing the whole recovery pass +- **Thinking block violations**: Recovers from API thinking block mismatches +- **Empty messages**: Reconstructs message history when content is missing +- **Context window limits**: Gracefully handles Claude context window exceeded errors with intelligent compaction +- **JSON parse errors**: Recovers from malformed tool outputs + +Recovery happens transparently during agent execution. You see the result, not the failure. ## Skills Skills provide specialized workflows with embedded MCP servers and detailed instructions. A Skill is a mechanism that injects **specialized knowledge (Context)** and **tools (MCP)** for specific domains into agents. @@ -844,7 +900,7 @@ When a skill MCP has `oauth` configured: Pre-authenticate via CLI: ```bash -bunx oh-my-openagent mcp oauth login --server-url https://api.example.com +bunx oh-my-opencode mcp oauth login --server-url https://api.example.com ``` ## Context Injection