diff --git a/packages/app/src/components/prompt-input/submit.ts b/packages/app/src/components/prompt-input/submit.ts index 9387bcb1db..77f605ebed 100644 --- a/packages/app/src/components/prompt-input/submit.ts +++ b/packages/app/src/components/prompt-input/submit.ts @@ -73,12 +73,16 @@ export function createPromptSubmit(input: PromptSubmitInput) { const abort = async () => { const sessionID = params.id if (!sessionID) return Promise.resolve() + + globalSync.todo.set(sessionID, []) + const [, setStore] = globalSync.child(sdk.directory) + setStore("todo", sessionID, []) + const queued = pending.get(sessionID) if (queued) { queued.abort.abort() queued.cleanup() pending.delete(sessionID) - globalSync.todo.set(sessionID, undefined) return Promise.resolve() } return sdk.client.session @@ -86,9 +90,6 @@ export function createPromptSubmit(input: PromptSubmitInput) { sessionID, }) .catch(() => {}) - .finally(() => { - globalSync.todo.set(sessionID, undefined) - }) } const restoreCommentItems = (items: CommentItem[]) => { diff --git a/packages/app/src/i18n/ar.ts b/packages/app/src/i18n/ar.ts index d35c63df77..44189b25ac 100644 --- a/packages/app/src/i18n/ar.ts +++ b/packages/app/src/i18n/ar.ts @@ -134,7 +134,8 @@ export const dict = { "provider.connect.oauth.code.invalid": "رمز التفويض غير صالح", "provider.connect.oauth.auto.visit.prefix": "قم بزيارة ", "provider.connect.oauth.auto.visit.link": "هذا الرابط", - "provider.connect.oauth.auto.visit.suffix": " وأدخل الرمز أدناه لتوصيل حسابك واستخدام نماذج {{provider}} في Kilo.", + "provider.connect.oauth.auto.visit.suffix": + " وأدخل الرمز أدناه لتوصيل حسابك واستخدام نماذج {{provider}} في Kilo.", "provider.connect.oauth.auto.confirmationCode": "رمز التأكيد", "provider.connect.toast.connected.title": "تم توصيل {{provider}}", "provider.connect.toast.connected.description": "نماذج {{provider}} متاحة الآن للاستخدام.", diff --git a/packages/app/src/i18n/fr.ts b/packages/app/src/i18n/fr.ts index eb21fd5c3b..ed2a879fd7 100644 --- a/packages/app/src/i18n/fr.ts +++ b/packages/app/src/i18n/fr.ts @@ -389,7 +389,8 @@ export const dict = { "toast.session.unshare.failed.description": "Une erreur s'est produite lors de l'annulation du partage de la session", "toast.session.listFailed.title": "Échec du chargement des sessions pour {{project}}", "toast.update.title": "Mise à jour disponible", - "toast.update.description": "Une nouvelle version d'Kilo ({{version}}) est maintenant disponible pour installation.", + "toast.update.description": + "Une nouvelle version d'Kilo ({{version}}) est maintenant disponible pour installation.", "toast.update.action.installRestart": "Installer et redémarrer", "toast.update.action.notYet": "Pas encore", "error.page.title": "Quelque chose s'est mal passé", @@ -523,7 +524,8 @@ export const dict = { "sidebar.workspaces.enable": "Activer les espaces de travail", "sidebar.workspaces.disable": "Désactiver les espaces de travail", "sidebar.gettingStarted.title": "Commencer", - "sidebar.gettingStarted.line1": "Kilo inclut des modèles gratuits pour que vous puissiez commencer immédiatement.", + "sidebar.gettingStarted.line1": + "Kilo inclut des modèles gratuits pour que vous puissiez commencer immédiatement.", "sidebar.gettingStarted.line2": "Connectez n'importe quel fournisseur pour utiliser des modèles, y compris Claude, GPT, Gemini etc.", "sidebar.project.recentSessions": "Sessions récentes", diff --git a/packages/app/src/i18n/th.ts b/packages/app/src/i18n/th.ts index 09dbc5bce2..243d1ce21b 100644 --- a/packages/app/src/i18n/th.ts +++ b/packages/app/src/i18n/th.ts @@ -146,7 +146,8 @@ export const dict = { "provider.connect.oauth.code.invalid": "รหัสการอนุญาตไม่ถูกต้อง", "provider.connect.oauth.auto.visit.prefix": "เยี่ยมชม ", "provider.connect.oauth.auto.visit.link": "ลิงก์นี้", - "provider.connect.oauth.auto.visit.suffix": " และป้อนรหัสด้านล่างเพื่อเชื่อมต่อบัญชีและใช้โมเดล {{provider}} ใน Kilo", + "provider.connect.oauth.auto.visit.suffix": + " และป้อนรหัสด้านล่างเพื่อเชื่อมต่อบัญชีและใช้โมเดล {{provider}} ใน Kilo", "provider.connect.oauth.auto.confirmationCode": "รหัสยืนยัน", "provider.connect.toast.connected.title": "{{provider}} ที่เชื่อมต่อแล้ว", "provider.connect.toast.connected.description": "โมเดล {{provider}} พร้อมใช้งานแล้ว", diff --git a/packages/app/src/i18n/zht.ts b/packages/app/src/i18n/zht.ts index 006ed3a57e..29a71b178b 100644 --- a/packages/app/src/i18n/zht.ts +++ b/packages/app/src/i18n/zht.ts @@ -147,7 +147,8 @@ export const dict = { "provider.connect.oauth.code.invalid": "授權碼無效", "provider.connect.oauth.auto.visit.prefix": "造訪 ", "provider.connect.oauth.auto.visit.link": "此連結", - "provider.connect.oauth.auto.visit.suffix": " 並輸入以下程式碼,以連線你的帳戶並在 Kilo 中使用 {{provider}} 模型。", + "provider.connect.oauth.auto.visit.suffix": + " 並輸入以下程式碼,以連線你的帳戶並在 Kilo 中使用 {{provider}} 模型。", "provider.connect.oauth.auto.confirmationCode": "確認碼", "provider.connect.toast.connected.title": "{{provider}} 已連線", "provider.connect.toast.connected.description": "現在可以使用 {{provider}} 模型了。", diff --git a/packages/app/src/pages/layout/sidebar-items.tsx b/packages/app/src/pages/layout/sidebar-items.tsx index 6fb7ce6319..0a461ca79f 100644 --- a/packages/app/src/pages/layout/sidebar-items.tsx +++ b/packages/app/src/pages/layout/sidebar-items.tsx @@ -32,7 +32,9 @@ export const ProjectIcon = (props: { project: LocalProject; class?: string; noti
0 && props.notify }} diff --git a/packages/app/src/pages/session.tsx b/packages/app/src/pages/session.tsx index 3235dde05e..4920d48d59 100644 --- a/packages/app/src/pages/session.tsx +++ b/packages/app/src/pages/session.tsx @@ -1199,6 +1199,7 @@ export default function Page() { comments.clear() resumeScroll() }} + onResponseSubmit={resumeScroll} setPromptDockRef={(el) => { promptDock = el }} diff --git a/packages/app/src/pages/session/composer/session-composer-region.tsx b/packages/app/src/pages/session/composer/session-composer-region.tsx index bb3abe5893..f0784b0c57 100644 --- a/packages/app/src/pages/session/composer/session-composer-region.tsx +++ b/packages/app/src/pages/session/composer/session-composer-region.tsx @@ -16,6 +16,7 @@ export function SessionComposerRegion(props: { newSessionWorktree: string onNewSessionWorktreeReset: () => void onSubmit: () => void + onResponseSubmit: () => void setPromptDockRef: (el: HTMLDivElement) => void onModeAction?: (input: { mode: string; text: string; description?: string }) => void // kilocode_change }) { @@ -59,7 +60,11 @@ export function SessionComposerRegion(props: { {(request) => (
{/* kilocode_change: add onModeAction */} - +
)} @@ -70,7 +75,10 @@ export function SessionComposerRegion(props: { { + props.onResponseSubmit() + props.state.decide(response) + }} />
)} diff --git a/packages/app/src/pages/session/composer/session-question-dock.tsx b/packages/app/src/pages/session/composer/session-question-dock.tsx index 3c11fd122b..88addeb63d 100644 --- a/packages/app/src/pages/session/composer/session-question-dock.tsx +++ b/packages/app/src/pages/session/composer/session-question-dock.tsx @@ -11,6 +11,7 @@ import { useSDK } from "@/context/sdk" // kilocode_change start - add onModeAction prop for mode-switching support export const SessionQuestionDock: Component<{ request: QuestionRequest + onSubmit: () => void onModeAction?: (input: { mode: string; text: string; description?: string }) => void }> = (props) => { // kilocode_change end @@ -120,6 +121,7 @@ export const SessionQuestionDock: Component<{ const reply = async (answers: QuestionAnswer[]) => { if (store.sending) return + props.onSubmit() setStore("sending", true) try { await sdk.client.question.reply({ requestID: props.request.id, answers }) @@ -133,6 +135,7 @@ export const SessionQuestionDock: Component<{ const reject = async () => { if (store.sending) return + props.onSubmit() setStore("sending", true) try { await sdk.client.question.reject({ requestID: props.request.id }) diff --git a/packages/desktop/src/index.tsx b/packages/desktop/src/index.tsx index 81015d5678..9e0ce8f0a6 100644 --- a/packages/desktop/src/index.tsx +++ b/packages/desktop/src/index.tsx @@ -472,12 +472,10 @@ render(() => { } return ( - - {(defaultServer) => ( - - - - )} + + + + ) }} @@ -492,19 +490,34 @@ type ServerReadyData = { url: string; password: string | null } // Gate component that waits for the server to be ready function ServerGate(props: { children: (data: Accessor) => JSX.Element }) { const [serverData] = createResource(() => commands.awaitInitialization(new Channel() as any)) - if (serverData.state === "errored") throw serverData.error return ( - +
+ +
+

Failed to start server

+

+ {String(serverData.error ?? "Unknown error")} +

+
} > - {(data) => props.children(data)} + + +
+
+ } + > + {(data) => props.children(data)} +
) } diff --git a/packages/opencode/package.json b/packages/opencode/package.json index ff768f55a7..23e00fe707 100644 --- a/packages/opencode/package.json +++ b/packages/opencode/package.json @@ -80,15 +80,13 @@ "@gitlab/opencode-gitlab-auth": "1.3.3", "@hono/standard-validator": "0.1.5", "@hono/zod-validator": "catalog:", - "@kilocode/kilo-gateway": "workspace:*", - "@kilocode/kilo-telemetry": "workspace:*", - "@kilocode/plugin": "workspace:*", - "@kilocode/sdk": "workspace:*", "@modelcontextprotocol/sdk": "1.25.2", "@octokit/graphql": "9.0.2", "@octokit/rest": "catalog:", "@openauthjs/openauth": "catalog:", + "@kilocode/plugin": "workspace:*", "@opencode-ai/script": "workspace:*", + "@kilocode/sdk": "workspace:*", "@opencode-ai/util": "workspace:*", "@openrouter/ai-sdk-provider": "1.5.4", "@opentui/core": "0.1.79", @@ -122,7 +120,6 @@ "opentui-spinner": "0.0.6", "partial-json": "0.1.7", "remeda": "catalog:", - "simple-git": "3.31.1", "solid-js": "catalog:", "strip-ansi": "7.1.2", "tree-sitter-bash": "0.25.0", @@ -133,7 +130,10 @@ "xdg-basedir": "5.1.0", "yargs": "18.0.0", "zod": "catalog:", - "zod-to-json-schema": "3.24.5" + "zod-to-json-schema": "3.24.5", + "@kilocode/kilo-gateway": "workspace:*", + "@kilocode/kilo-telemetry": "workspace:*", + "simple-git": "3.31.1" }, "overrides": { "drizzle-orm": "1.0.0-beta.12-a5629fb" diff --git a/packages/opencode/src/cli/cmd/tui/routes/session/index.tsx b/packages/opencode/src/cli/cmd/tui/routes/session/index.tsx index 299d515ed3..4910b1d41d 100644 --- a/packages/opencode/src/cli/cmd/tui/routes/session/index.tsx +++ b/packages/opencode/src/cli/cmd/tui/routes/session/index.tsx @@ -100,6 +100,7 @@ const context = createContext<{ showThinking: () => boolean showTimestamps: () => boolean showDetails: () => boolean + showGenericToolOutput: () => boolean diffWrapMode: () => "word" | "none" sync: ReturnType }>() @@ -154,6 +155,7 @@ export function Session() { const [showHeader, setShowHeader] = kv.signal("header_visible", true) const [diffWrapMode] = kv.signal<"word" | "none">("diff_wrap_mode", "word") const [animationsEnabled, setAnimationsEnabled] = kv.signal("animations_enabled", true) + const [showGenericToolOutput, setShowGenericToolOutput] = kv.signal("generic_tool_output_visibility", false) const wide = createMemo(() => dimensions().width > 120) const sidebarVisible = createMemo(() => { @@ -606,6 +608,15 @@ export function Session() { dialog.clear() }, }, + { + title: showGenericToolOutput() ? "Hide generic tool output" : "Show generic tool output", + value: "session.toggle.generic_tool_output", + category: "Session", + onSelect: (dialog) => { + setShowGenericToolOutput((prev) => !prev) + dialog.clear() + }, + }, { title: "Page up", value: "session.page.up", @@ -980,6 +991,7 @@ export function Session() { showThinking, showTimestamps, showDetails, + showGenericToolOutput, diffWrapMode, sync, }} @@ -1517,10 +1529,40 @@ type ToolProps = { part: ToolPart } function GenericTool(props: ToolProps) { + const { theme } = useTheme() + const ctx = use() + const output = createMemo(() => props.output?.trim() ?? "") + const [expanded, setExpanded] = createSignal(false) + const lines = createMemo(() => output().split("\n")) + const maxLines = 3 + const overflow = createMemo(() => lines().length > maxLines) + const limited = createMemo(() => { + if (expanded() || !overflow()) return output() + return [...lines().slice(0, maxLines), "…"].join("\n") + }) + return ( - - {props.tool} {input(props.input)} - + + {props.tool} {input(props.input)} + + } + > + setExpanded((prev) => !prev) : undefined} + > + + {limited()} + + {expanded() ? "Click to collapse" : "Click to expand"} + + + + ) } diff --git a/packages/opencode/src/provider/transform.ts b/packages/opencode/src/provider/transform.ts index 146d14e2d0..7f6c44b858 100644 --- a/packages/opencode/src/provider/transform.ts +++ b/packages/opencode/src/provider/transform.ts @@ -349,6 +349,10 @@ export namespace ProviderTransform { if (!model.capabilities.reasoning) return {} const id = model.id.toLowerCase() + const isAnthropicAdaptive = ["opus-4-6", "opus-4.6", "sonnet-4-6", "sonnet-4.6"].some((v) => + model.api.id.includes(v), + ) + const adaptiveEfforts = ["low", "medium", "high", "max"] if ( id.includes("deepseek") || id.includes("minimax") || @@ -401,6 +405,19 @@ export namespace ProviderTransform { // TODO: YOU CANNOT SET max_tokens if this is set!!! case "@ai-sdk/gateway": if (model.id.includes("anthropic")) { + if (isAnthropicAdaptive) { + return Object.fromEntries( + adaptiveEfforts.map((effort) => [ + effort, + { + thinking: { + type: "adaptive", + }, + effort, + }, + ]), + ) + } return { high: { thinking: { @@ -537,10 +554,9 @@ export namespace ProviderTransform { case "@ai-sdk/google-vertex/anthropic": // https://v5.ai-sdk.dev/providers/ai-sdk-providers/google-vertex#anthropic-provider - if (model.api.id.includes("opus-4-6") || model.api.id.includes("opus-4.6")) { - const efforts = ["low", "medium", "high", "max"] + if (isAnthropicAdaptive) { return Object.fromEntries( - efforts.map((effort) => [ + adaptiveEfforts.map((effort) => [ effort, { thinking: { @@ -569,10 +585,9 @@ export namespace ProviderTransform { case "@ai-sdk/amazon-bedrock": // https://v5.ai-sdk.dev/providers/ai-sdk-providers/amazon-bedrock - if (model.api.id.includes("opus-4-6") || model.api.id.includes("opus-4.6")) { - const efforts = ["low", "medium", "high", "max"] + if (isAnthropicAdaptive) { return Object.fromEntries( - efforts.map((effort) => [ + adaptiveEfforts.map((effort) => [ effort, { reasoningConfig: { diff --git a/packages/opencode/test/provider/transform.test.ts b/packages/opencode/test/provider/transform.test.ts index 8e7b08a65b..c18698e5a3 100644 --- a/packages/opencode/test/provider/transform.test.ts +++ b/packages/opencode/test/provider/transform.test.ts @@ -1022,79 +1022,6 @@ describe("ProviderTransform.message - anthropic empty content filtering", () => }) }) -describe("ProviderTransform.message - OpenRouter/Kilo Gateway thinking stripping", () => { - const claudeViaGateway = { - id: "openrouter/anthropic/claude-3-5-sonnet", - providerID: "openrouter", - api: { - id: "anthropic/claude-3-5-sonnet", - url: "https://openrouter.ai", - npm: "@kilocode/kilo-gateway", - }, - name: "Claude 3.5 Sonnet (Gateway)", - capabilities: { - temperature: true, - reasoning: true, - attachment: true, - toolcall: true, - input: { text: true, audio: false, image: true, video: false, pdf: true }, - output: { text: true, audio: false, image: false, video: false, pdf: false }, - interleaved: false, - }, - cost: { - input: 0, - output: 0, - cache: { read: 0, write: 0 }, - }, - limit: { - context: 200000, - output: 8192, - }, - status: "active", - options: {}, - headers: {}, - } as any - - test("drops messages that become empty after stripping thinking/reasoning", () => { - const msgs = [ - { role: "user", content: "Hello" }, - { - role: "assistant", - content: [{ type: "thinking", text: "..." }, { type: "reasoning", text: "..." }, { type: "redacted_thinking" }], - }, - { role: "user", content: "World" }, - ] as any[] - - const result = ProviderTransform.message(msgs, claudeViaGateway, {}) - - expect(result).toHaveLength(2) - expect(result[0].content).toBe("Hello") - expect(result[1].content).toBe("World") - }) - - test("keeps messages that still contain supported parts after stripping", () => { - const msgs = [ - { - role: "assistant", - content: [ - { type: "thinking", text: "..." }, - { type: "text", text: "Answer" }, - ], - }, - ] as any[] - - const result = ProviderTransform.message(msgs, claudeViaGateway, {}) - - expect(result).toHaveLength(1) - expect(result[0].content).toHaveLength(1) - // applyCaching adds providerOptions because model.api.id includes "anthropic" - const part = result[0].content[0] as any - expect(part.type).toEqual("text") - expect(part.text).toEqual("Answer") - expect(part.providerOptions?.openrouter?.cacheControl).toEqual({ type: "ephemeral" }) - }) -}) - describe("ProviderTransform.message - strip openai metadata when store=false", () => { const openaiModel = { id: "openai/gpt-5", @@ -1637,746 +1564,902 @@ describe("ProviderTransform.variants", () => { ...overrides, }) - describe("ProviderTransform.variants", () => { - test("returns empty object when model has no reasoning capabilities", () => { - const model = createMockModel({ - capabilities: { reasoning: false }, - }) - const result = ProviderTransform.variants(model) - expect(result).toEqual({}) + test("returns empty object when model has no reasoning capabilities", () => { + const model = createMockModel({ + capabilities: { reasoning: false }, }) + const result = ProviderTransform.variants(model) + expect(result).toEqual({}) + }) - test("deepseek returns empty object", () => { + test("deepseek returns empty object", () => { + const model = createMockModel({ + id: "deepseek/deepseek-chat", + providerID: "deepseek", + api: { + id: "deepseek-chat", + url: "https://api.deepseek.com", + npm: "@ai-sdk/openai-compatible", + }, + }) + const result = ProviderTransform.variants(model) + expect(result).toEqual({}) + }) + + test("minimax returns empty object", () => { + const model = createMockModel({ + id: "minimax/minimax-model", + providerID: "minimax", + api: { + id: "minimax-model", + url: "https://api.minimax.com", + npm: "@ai-sdk/openai-compatible", + }, + }) + const result = ProviderTransform.variants(model) + expect(result).toEqual({}) + }) + + test("glm returns empty object", () => { + const model = createMockModel({ + id: "glm/glm-4", + providerID: "glm", + api: { + id: "glm-4", + url: "https://api.glm.com", + npm: "@ai-sdk/openai-compatible", + }, + }) + const result = ProviderTransform.variants(model) + expect(result).toEqual({}) + }) + + test("mistral returns empty object", () => { + const model = createMockModel({ + id: "mistral/mistral-large", + providerID: "mistral", + api: { + id: "mistral-large-latest", + url: "https://api.mistral.com", + npm: "@ai-sdk/mistral", + }, + }) + const result = ProviderTransform.variants(model) + expect(result).toEqual({}) + }) + + describe("@openrouter/ai-sdk-provider", () => { + test("returns empty object for non-qualifying models", () => { const model = createMockModel({ - id: "deepseek/deepseek-chat", - providerID: "deepseek", + id: "openrouter/test-model", + providerID: "openrouter", api: { - id: "deepseek-chat", - url: "https://api.deepseek.com", - npm: "@ai-sdk/openai-compatible", + id: "test-model", + url: "https://openrouter.ai", + npm: "@openrouter/ai-sdk-provider", }, }) const result = ProviderTransform.variants(model) expect(result).toEqual({}) }) - test("minimax returns empty object", () => { + test("gpt models return OPENAI_EFFORTS with reasoning", () => { const model = createMockModel({ - id: "minimax/minimax-model", - providerID: "minimax", + id: "openrouter/gpt-4", + providerID: "openrouter", api: { - id: "minimax-model", - url: "https://api.minimax.com", - npm: "@ai-sdk/openai-compatible", + id: "gpt-4", + url: "https://openrouter.ai", + npm: "@openrouter/ai-sdk-provider", + }, + }) + const result = ProviderTransform.variants(model) + expect(Object.keys(result)).toEqual(["none", "minimal", "low", "medium", "high", "xhigh"]) + expect(result.low).toEqual({ reasoning: { effort: "low" } }) + expect(result.high).toEqual({ reasoning: { effort: "high" } }) + }) + + test("gemini-3 returns OPENAI_EFFORTS with reasoning", () => { + const model = createMockModel({ + id: "openrouter/gemini-3-5-pro", + providerID: "openrouter", + api: { + id: "gemini-3-5-pro", + url: "https://openrouter.ai", + npm: "@openrouter/ai-sdk-provider", + }, + }) + const result = ProviderTransform.variants(model) + expect(Object.keys(result)).toEqual(["none", "minimal", "low", "medium", "high", "xhigh"]) + }) + + test("grok-4 returns empty object", () => { + const model = createMockModel({ + id: "openrouter/grok-4", + providerID: "openrouter", + api: { + id: "grok-4", + url: "https://openrouter.ai", + npm: "@openrouter/ai-sdk-provider", }, }) const result = ProviderTransform.variants(model) expect(result).toEqual({}) }) - test("glm returns empty object", () => { + test("grok-3-mini returns low and high with reasoning", () => { const model = createMockModel({ - id: "glm/glm-4", - providerID: "glm", + id: "openrouter/grok-3-mini", + providerID: "openrouter", api: { - id: "glm-4", - url: "https://api.glm.com", - npm: "@ai-sdk/openai-compatible", + id: "grok-3-mini", + url: "https://openrouter.ai", + npm: "@openrouter/ai-sdk-provider", }, }) const result = ProviderTransform.variants(model) - expect(result).toEqual({}) + expect(Object.keys(result)).toEqual(["low", "high"]) + expect(result.low).toEqual({ reasoning: { effort: "low" } }) + expect(result.high).toEqual({ reasoning: { effort: "high" } }) }) + }) - test("mistral returns empty object", () => { + // kilocode_change start + describe("@kilocode/kilo-gateway", () => { + test("claude models return empty variants (reasoning disabled)", () => { const model = createMockModel({ - id: "mistral/mistral-large", - providerID: "mistral", + id: "kilo/anthropic/claude-sonnet-4", + providerID: "kilo", api: { - id: "mistral-large-latest", - url: "https://api.mistral.com", - npm: "@ai-sdk/mistral", - }, - }) - const result = ProviderTransform.variants(model) - expect(result).toEqual({}) - }) - - describe("@openrouter/ai-sdk-provider", () => { - test("returns empty object for non-qualifying models", () => { - const model = createMockModel({ - id: "openrouter/test-model", - providerID: "openrouter", - api: { - id: "test-model", - url: "https://openrouter.ai", - npm: "@openrouter/ai-sdk-provider", - }, - }) - const result = ProviderTransform.variants(model) - expect(result).toEqual({}) - }) - - test("gpt models return OPENAI_EFFORTS with reasoning", () => { - const model = createMockModel({ - id: "openrouter/gpt-4", - providerID: "openrouter", - api: { - id: "gpt-4", - url: "https://openrouter.ai", - npm: "@openrouter/ai-sdk-provider", - }, - }) - const result = ProviderTransform.variants(model) - expect(Object.keys(result)).toEqual(["none", "minimal", "low", "medium", "high", "xhigh"]) - expect(result.low).toEqual({ reasoning: { effort: "low" } }) - expect(result.high).toEqual({ reasoning: { effort: "high" } }) - }) - - test("gemini-3 returns OPENAI_EFFORTS with reasoning", () => { - const model = createMockModel({ - id: "openrouter/gemini-3-5-pro", - providerID: "openrouter", - api: { - id: "gemini-3-5-pro", - url: "https://openrouter.ai", - npm: "@openrouter/ai-sdk-provider", - }, - }) - const result = ProviderTransform.variants(model) - expect(Object.keys(result)).toEqual(["none", "minimal", "low", "medium", "high", "xhigh"]) - }) - - test("grok-4 returns empty object", () => { - const model = createMockModel({ - id: "openrouter/grok-4", - providerID: "openrouter", - api: { - id: "grok-4", - url: "https://openrouter.ai", - npm: "@openrouter/ai-sdk-provider", - }, - }) - const result = ProviderTransform.variants(model) - expect(result).toEqual({}) - }) - - test("grok-3-mini returns low and high with reasoning", () => { - const model = createMockModel({ - id: "openrouter/grok-3-mini", - providerID: "openrouter", - api: { - id: "grok-3-mini", - url: "https://openrouter.ai", - npm: "@openrouter/ai-sdk-provider", - }, - }) - const result = ProviderTransform.variants(model) - expect(Object.keys(result)).toEqual(["low", "high"]) - expect(result.low).toEqual({ reasoning: { effort: "low" } }) - expect(result.high).toEqual({ reasoning: { effort: "high" } }) - }) - }) - - // kilocode_change start - describe("@kilocode/kilo-gateway", () => { - test("claude models return empty variants (reasoning disabled)", () => { - const model = createMockModel({ - id: "kilo/anthropic/claude-sonnet-4", - providerID: "kilo", - api: { - id: "anthropic/claude-sonnet-4", - url: "https://gateway.kilo.ai", - npm: "@kilocode/kilo-gateway", - }, - }) - const result = ProviderTransform.variants(model) - expect(Object.keys(result)).toEqual([]) - }) - - test("anthropic models in api.id return empty variants (reasoning disabled)", () => { - const model = createMockModel({ - id: "kilo/anthropic/claude-opus-4", - providerID: "kilo", - api: { - id: "anthropic/claude-opus-4", - url: "https://gateway.kilo.ai", - npm: "@kilocode/kilo-gateway", - }, - }) - const result = ProviderTransform.variants(model) - expect(Object.keys(result)).toEqual([]) - }) - - test("gpt models return OPENAI_EFFORTS with reasoning and encrypted content", () => { - const model = createMockModel({ - id: "kilo/openai/gpt-5", - providerID: "kilo", - api: { - id: "openai/gpt-5", - url: "https://gateway.kilo.ai", - npm: "@kilocode/kilo-gateway", - }, - }) - const result = ProviderTransform.variants(model) - expect(Object.keys(result)).toEqual(["none", "minimal", "low", "medium", "high", "xhigh"]) - expect(result.low).toEqual({ - reasoningEffort: "low", - reasoningSummary: "auto", - include: ["reasoning.encrypted_content"], - }) - }) - - test("gemini-3 models return OPENAI_EFFORTS with reasoning and encrypted content", () => { - const model = createMockModel({ - id: "kilo/google/gemini-3-pro", - providerID: "kilo", - api: { - id: "google/gemini-3-pro", - url: "https://gateway.kilo.ai", - npm: "@kilocode/kilo-gateway", - }, - }) - const result = ProviderTransform.variants(model) - expect(Object.keys(result)).toEqual(["none", "minimal", "low", "medium", "high", "xhigh"]) - }) - - test("non-qualifying models return empty object", () => { - const model = createMockModel({ - id: "kilo/meta/llama-4", - providerID: "kilo", - api: { - id: "meta/llama-4", - url: "https://gateway.kilo.ai", - npm: "@kilocode/kilo-gateway", - }, - }) - const result = ProviderTransform.variants(model) - expect(result).toEqual({}) - }) - - test("grok-3-mini returns low and high with reasoning", () => { - const model = createMockModel({ - id: "kilo/x-ai/grok-3-mini", - providerID: "kilo", - api: { - id: "x-ai/grok-3-mini", - url: "https://gateway.kilo.ai", - npm: "@kilocode/kilo-gateway", - }, - }) - const result = ProviderTransform.variants(model) - expect(Object.keys(result)).toEqual(["low", "high"]) - expect(result.low).toEqual({ reasoning: { effort: "low" } }) - expect(result.high).toEqual({ reasoning: { effort: "high" } }) - }) - - test("codex models return OPENAI_EFFORTS with object-based reasoning format", () => { - const model = createMockModel({ - id: "kilo/openai/gpt-5.2-codex", - providerID: "kilo", - api: { - id: "openai/gpt-5.2-codex", - url: "https://gateway.kilo.ai", - npm: "@kilocode/kilo-gateway", - }, - }) - const result = ProviderTransform.variants(model) - expect(Object.keys(result)).toEqual(["none", "minimal", "low", "medium", "high", "xhigh"]) - expect(result.low).toEqual({ reasoning: { effort: "low" } }) - expect(result.high).toEqual({ reasoning: { effort: "high" } }) - expect(result.xhigh).toEqual({ reasoning: { effort: "xhigh" } }) - }) - }) - // kilocode_change end - - describe("@ai-sdk/gateway", () => { - test("anthropic models return anthropic thinking options", () => { - const model = createMockModel({ id: "anthropic/claude-sonnet-4", - providerID: "gateway", - api: { - id: "anthropic/claude-sonnet-4", - url: "https://gateway.ai", - npm: "@ai-sdk/gateway", - }, - }) - const result = ProviderTransform.variants(model) - expect(Object.keys(result)).toEqual(["high", "max"]) - expect(result.high).toEqual({ - thinking: { - type: "enabled", - budgetTokens: 16000, - }, - }) - expect(result.max).toEqual({ - thinking: { - type: "enabled", - budgetTokens: 31999, - }, - }) + url: "https://gateway.kilo.ai", + npm: "@kilocode/kilo-gateway", + }, }) + const result = ProviderTransform.variants(model) + expect(Object.keys(result)).toEqual([]) + }) - test("returns OPENAI_EFFORTS with reasoningEffort", () => { - const model = createMockModel({ - id: "gateway/gateway-model", - providerID: "gateway", - api: { - id: "gateway-model", - url: "https://gateway.ai", - npm: "@ai-sdk/gateway", - }, - }) - const result = ProviderTransform.variants(model) - expect(Object.keys(result)).toEqual(["none", "minimal", "low", "medium", "high", "xhigh"]) - expect(result.low).toEqual({ reasoningEffort: "low" }) - expect(result.high).toEqual({ reasoningEffort: "high" }) + test("anthropic models in api.id return empty variants (reasoning disabled)", () => { + const model = createMockModel({ + id: "kilo/anthropic/claude-opus-4", + providerID: "kilo", + api: { + id: "anthropic/claude-opus-4", + url: "https://gateway.kilo.ai", + npm: "@kilocode/kilo-gateway", + }, + }) + const result = ProviderTransform.variants(model) + expect(Object.keys(result)).toEqual([]) + }) + + test("gpt models return OPENAI_EFFORTS with reasoning and encrypted content", () => { + const model = createMockModel({ + id: "kilo/openai/gpt-5", + providerID: "kilo", + api: { + id: "openai/gpt-5", + url: "https://gateway.kilo.ai", + npm: "@kilocode/kilo-gateway", + }, + }) + const result = ProviderTransform.variants(model) + expect(Object.keys(result)).toEqual(["none", "minimal", "low", "medium", "high", "xhigh"]) + expect(result.low).toEqual({ + reasoningEffort: "low", + reasoningSummary: "auto", + include: ["reasoning.encrypted_content"], }) }) - describe("@ai-sdk/github-copilot", () => { - test("standard models return low, medium, high", () => { - const model = createMockModel({ + test("gemini-3 models return OPENAI_EFFORTS with reasoning and encrypted content", () => { + const model = createMockModel({ + id: "kilo/google/gemini-3-pro", + providerID: "kilo", + api: { + id: "google/gemini-3-pro", + url: "https://gateway.kilo.ai", + npm: "@kilocode/kilo-gateway", + }, + }) + const result = ProviderTransform.variants(model) + expect(Object.keys(result)).toEqual(["none", "minimal", "low", "medium", "high", "xhigh"]) + }) + + test("non-qualifying models return empty object", () => { + const model = createMockModel({ + id: "kilo/meta/llama-4", + providerID: "kilo", + api: { + id: "meta/llama-4", + url: "https://gateway.kilo.ai", + npm: "@kilocode/kilo-gateway", + }, + }) + const result = ProviderTransform.variants(model) + expect(result).toEqual({}) + }) + + test("grok-3-mini returns low and high with reasoning", () => { + const model = createMockModel({ + id: "kilo/x-ai/grok-3-mini", + providerID: "kilo", + api: { + id: "x-ai/grok-3-mini", + url: "https://gateway.kilo.ai", + npm: "@kilocode/kilo-gateway", + }, + }) + const result = ProviderTransform.variants(model) + expect(Object.keys(result)).toEqual(["low", "high"]) + expect(result.low).toEqual({ reasoning: { effort: "low" } }) + expect(result.high).toEqual({ reasoning: { effort: "high" } }) + }) + + test("codex models return OPENAI_EFFORTS with object-based reasoning format", () => { + const model = createMockModel({ + id: "kilo/openai/gpt-5.2-codex", + providerID: "kilo", + api: { + id: "openai/gpt-5.2-codex", + url: "https://gateway.kilo.ai", + npm: "@kilocode/kilo-gateway", + }, + }) + const result = ProviderTransform.variants(model) + expect(Object.keys(result)).toEqual(["none", "minimal", "low", "medium", "high", "xhigh"]) + expect(result.low).toEqual({ reasoning: { effort: "low" } }) + expect(result.high).toEqual({ reasoning: { effort: "high" } }) + expect(result.xhigh).toEqual({ reasoning: { effort: "xhigh" } }) + }) + }) + // kilocode_change end + + describe("@ai-sdk/gateway", () => { + test("anthropic sonnet 4.6 models return adaptive thinking options", () => { + const model = createMockModel({ + id: "anthropic/claude-sonnet-4-6", + providerID: "gateway", + api: { + id: "anthropic/claude-sonnet-4-6", + url: "https://gateway.ai", + npm: "@ai-sdk/gateway", + }, + }) + const result = ProviderTransform.variants(model) + expect(Object.keys(result)).toEqual(["low", "medium", "high", "max"]) + expect(result.medium).toEqual({ + thinking: { + type: "adaptive", + }, + effort: "medium", + }) + }) + + test("anthropic sonnet 4.6 dot-format models return adaptive thinking options", () => { + const model = createMockModel({ + id: "anthropic/claude-sonnet-4-6", + providerID: "gateway", + api: { + id: "anthropic/claude-sonnet-4.6", + url: "https://gateway.ai", + npm: "@ai-sdk/gateway", + }, + }) + const result = ProviderTransform.variants(model) + expect(Object.keys(result)).toEqual(["low", "medium", "high", "max"]) + expect(result.medium).toEqual({ + thinking: { + type: "adaptive", + }, + effort: "medium", + }) + }) + + test("anthropic opus 4.6 dot-format models return adaptive thinking options", () => { + const model = createMockModel({ + id: "anthropic/claude-opus-4-6", + providerID: "gateway", + api: { + id: "anthropic/claude-opus-4.6", + url: "https://gateway.ai", + npm: "@ai-sdk/gateway", + }, + }) + const result = ProviderTransform.variants(model) + expect(Object.keys(result)).toEqual(["low", "medium", "high", "max"]) + expect(result.high).toEqual({ + thinking: { + type: "adaptive", + }, + effort: "high", + }) + }) + + test("anthropic models return anthropic thinking options", () => { + const model = createMockModel({ + id: "anthropic/claude-sonnet-4", + providerID: "gateway", + api: { + id: "anthropic/claude-sonnet-4", + url: "https://gateway.ai", + npm: "@ai-sdk/gateway", + }, + }) + const result = ProviderTransform.variants(model) + expect(Object.keys(result)).toEqual(["high", "max"]) + expect(result.high).toEqual({ + thinking: { + type: "enabled", + budgetTokens: 16000, + }, + }) + expect(result.max).toEqual({ + thinking: { + type: "enabled", + budgetTokens: 31999, + }, + }) + }) + + test("returns OPENAI_EFFORTS with reasoningEffort", () => { + const model = createMockModel({ + id: "gateway/gateway-model", + providerID: "gateway", + api: { + id: "gateway-model", + url: "https://gateway.ai", + npm: "@ai-sdk/gateway", + }, + }) + const result = ProviderTransform.variants(model) + expect(Object.keys(result)).toEqual(["none", "minimal", "low", "medium", "high", "xhigh"]) + expect(result.low).toEqual({ reasoningEffort: "low" }) + expect(result.high).toEqual({ reasoningEffort: "high" }) + }) + }) + + describe("@ai-sdk/github-copilot", () => { + test("standard models return low, medium, high", () => { + const model = createMockModel({ + id: "gpt-4.5", + providerID: "github-copilot", + api: { id: "gpt-4.5", - providerID: "github-copilot", - api: { - id: "gpt-4.5", - url: "https://api.githubcopilot.com", - npm: "@ai-sdk/github-copilot", - }, - }) - const result = ProviderTransform.variants(model) - expect(Object.keys(result)).toEqual(["low", "medium", "high"]) - expect(result.low).toEqual({ - reasoningEffort: "low", - reasoningSummary: "auto", - include: ["reasoning.encrypted_content"], - }) + url: "https://api.githubcopilot.com", + npm: "@ai-sdk/github-copilot", + }, }) + const result = ProviderTransform.variants(model) + expect(Object.keys(result)).toEqual(["low", "medium", "high"]) + expect(result.low).toEqual({ + reasoningEffort: "low", + reasoningSummary: "auto", + include: ["reasoning.encrypted_content"], + }) + }) - test("gpt-5.1-codex-max includes xhigh", () => { - const model = createMockModel({ + test("gpt-5.1-codex-max includes xhigh", () => { + const model = createMockModel({ + id: "gpt-5.1-codex-max", + providerID: "github-copilot", + api: { id: "gpt-5.1-codex-max", - providerID: "github-copilot", - api: { - id: "gpt-5.1-codex-max", - url: "https://api.githubcopilot.com", - npm: "@ai-sdk/github-copilot", - }, - }) - const result = ProviderTransform.variants(model) - expect(Object.keys(result)).toEqual(["low", "medium", "high", "xhigh"]) + url: "https://api.githubcopilot.com", + npm: "@ai-sdk/github-copilot", + }, }) + const result = ProviderTransform.variants(model) + expect(Object.keys(result)).toEqual(["low", "medium", "high", "xhigh"]) + }) - test("gpt-5.1-codex-mini does not include xhigh", () => { - const model = createMockModel({ + test("gpt-5.1-codex-mini does not include xhigh", () => { + const model = createMockModel({ + id: "gpt-5.1-codex-mini", + providerID: "github-copilot", + api: { id: "gpt-5.1-codex-mini", - providerID: "github-copilot", - api: { - id: "gpt-5.1-codex-mini", - url: "https://api.githubcopilot.com", - npm: "@ai-sdk/github-copilot", - }, - }) - const result = ProviderTransform.variants(model) - expect(Object.keys(result)).toEqual(["low", "medium", "high"]) + url: "https://api.githubcopilot.com", + npm: "@ai-sdk/github-copilot", + }, }) + const result = ProviderTransform.variants(model) + expect(Object.keys(result)).toEqual(["low", "medium", "high"]) + }) - test("gpt-5.1-codex does not include xhigh", () => { - const model = createMockModel({ + test("gpt-5.1-codex does not include xhigh", () => { + const model = createMockModel({ + id: "gpt-5.1-codex", + providerID: "github-copilot", + api: { id: "gpt-5.1-codex", - providerID: "github-copilot", - api: { - id: "gpt-5.1-codex", - url: "https://api.githubcopilot.com", - npm: "@ai-sdk/github-copilot", - }, - }) - const result = ProviderTransform.variants(model) - expect(Object.keys(result)).toEqual(["low", "medium", "high"]) + url: "https://api.githubcopilot.com", + npm: "@ai-sdk/github-copilot", + }, }) + const result = ProviderTransform.variants(model) + expect(Object.keys(result)).toEqual(["low", "medium", "high"]) + }) - test("gpt-5.2 includes xhigh", () => { - const model = createMockModel({ + test("gpt-5.2 includes xhigh", () => { + const model = createMockModel({ + id: "gpt-5.2", + providerID: "github-copilot", + api: { id: "gpt-5.2", - providerID: "github-copilot", - api: { - id: "gpt-5.2", - url: "https://api.githubcopilot.com", - npm: "@ai-sdk/github-copilot", - }, - }) - const result = ProviderTransform.variants(model) - expect(Object.keys(result)).toEqual(["low", "medium", "high", "xhigh"]) - expect(result.xhigh).toEqual({ - reasoningEffort: "xhigh", - reasoningSummary: "auto", - include: ["reasoning.encrypted_content"], - }) + url: "https://api.githubcopilot.com", + npm: "@ai-sdk/github-copilot", + }, }) + const result = ProviderTransform.variants(model) + expect(Object.keys(result)).toEqual(["low", "medium", "high", "xhigh"]) + expect(result.xhigh).toEqual({ + reasoningEffort: "xhigh", + reasoningSummary: "auto", + include: ["reasoning.encrypted_content"], + }) + }) - test("gpt-5.2-codex includes xhigh", () => { - const model = createMockModel({ + test("gpt-5.2-codex includes xhigh", () => { + const model = createMockModel({ + id: "gpt-5.2-codex", + providerID: "github-copilot", + api: { id: "gpt-5.2-codex", - providerID: "github-copilot", - api: { - id: "gpt-5.2-codex", - url: "https://api.githubcopilot.com", - npm: "@ai-sdk/github-copilot", - }, - }) - const result = ProviderTransform.variants(model) - expect(Object.keys(result)).toEqual(["low", "medium", "high", "xhigh"]) + url: "https://api.githubcopilot.com", + npm: "@ai-sdk/github-copilot", + }, }) + const result = ProviderTransform.variants(model) + expect(Object.keys(result)).toEqual(["low", "medium", "high", "xhigh"]) + }) + }) + + describe("@ai-sdk/cerebras", () => { + test("returns WIDELY_SUPPORTED_EFFORTS with reasoningEffort", () => { + const model = createMockModel({ + id: "cerebras/llama-4", + providerID: "cerebras", + api: { + id: "llama-4-sc", + url: "https://api.cerebras.ai", + npm: "@ai-sdk/cerebras", + }, + }) + const result = ProviderTransform.variants(model) + expect(Object.keys(result)).toEqual(["low", "medium", "high"]) + expect(result.low).toEqual({ reasoningEffort: "low" }) + expect(result.high).toEqual({ reasoningEffort: "high" }) + }) + }) + + describe("@ai-sdk/togetherai", () => { + test("returns WIDELY_SUPPORTED_EFFORTS with reasoningEffort", () => { + const model = createMockModel({ + id: "togetherai/llama-4", + providerID: "togetherai", + api: { + id: "llama-4-sc", + url: "https://api.togetherai.com", + npm: "@ai-sdk/togetherai", + }, + }) + const result = ProviderTransform.variants(model) + expect(Object.keys(result)).toEqual(["low", "medium", "high"]) + expect(result.low).toEqual({ reasoningEffort: "low" }) + expect(result.high).toEqual({ reasoningEffort: "high" }) + }) + }) + + describe("@ai-sdk/xai", () => { + test("grok-3 returns empty object", () => { + const model = createMockModel({ + id: "xai/grok-3", + providerID: "xai", + api: { + id: "grok-3", + url: "https://api.x.ai", + npm: "@ai-sdk/xai", + }, + }) + const result = ProviderTransform.variants(model) + expect(result).toEqual({}) }) - describe("@ai-sdk/cerebras", () => { - test("returns WIDELY_SUPPORTED_EFFORTS with reasoningEffort", () => { - const model = createMockModel({ - id: "cerebras/llama-4", - providerID: "cerebras", - api: { - id: "llama-4-sc", - url: "https://api.cerebras.ai", - npm: "@ai-sdk/cerebras", - }, - }) - const result = ProviderTransform.variants(model) - expect(Object.keys(result)).toEqual(["low", "medium", "high"]) - expect(result.low).toEqual({ reasoningEffort: "low" }) - expect(result.high).toEqual({ reasoningEffort: "high" }) + test("grok-3-mini returns low and high with reasoningEffort", () => { + const model = createMockModel({ + id: "xai/grok-3-mini", + providerID: "xai", + api: { + id: "grok-3-mini", + url: "https://api.x.ai", + npm: "@ai-sdk/xai", + }, }) + const result = ProviderTransform.variants(model) + expect(Object.keys(result)).toEqual(["low", "high"]) + expect(result.low).toEqual({ reasoningEffort: "low" }) + expect(result.high).toEqual({ reasoningEffort: "high" }) }) + }) - describe("@ai-sdk/togetherai", () => { - test("returns WIDELY_SUPPORTED_EFFORTS with reasoningEffort", () => { - const model = createMockModel({ - id: "togetherai/llama-4", - providerID: "togetherai", - api: { - id: "llama-4-sc", - url: "https://api.togetherai.com", - npm: "@ai-sdk/togetherai", - }, - }) - const result = ProviderTransform.variants(model) - expect(Object.keys(result)).toEqual(["low", "medium", "high"]) - expect(result.low).toEqual({ reasoningEffort: "low" }) - expect(result.high).toEqual({ reasoningEffort: "high" }) + describe("@ai-sdk/deepinfra", () => { + test("returns WIDELY_SUPPORTED_EFFORTS with reasoningEffort", () => { + const model = createMockModel({ + id: "deepinfra/llama-4", + providerID: "deepinfra", + api: { + id: "llama-4-sc", + url: "https://api.deepinfra.com", + npm: "@ai-sdk/deepinfra", + }, }) + const result = ProviderTransform.variants(model) + expect(Object.keys(result)).toEqual(["low", "medium", "high"]) + expect(result.low).toEqual({ reasoningEffort: "low" }) + expect(result.high).toEqual({ reasoningEffort: "high" }) }) + }) - describe("@ai-sdk/xai", () => { - test("grok-3 returns empty object", () => { - const model = createMockModel({ - id: "xai/grok-3", - providerID: "xai", - api: { - id: "grok-3", - url: "https://api.x.ai", - npm: "@ai-sdk/xai", - }, - }) - const result = ProviderTransform.variants(model) - expect(result).toEqual({}) - }) - - test("grok-3-mini returns low and high with reasoningEffort", () => { - const model = createMockModel({ - id: "xai/grok-3-mini", - providerID: "xai", - api: { - id: "grok-3-mini", - url: "https://api.x.ai", - npm: "@ai-sdk/xai", - }, - }) - const result = ProviderTransform.variants(model) - expect(Object.keys(result)).toEqual(["low", "high"]) - expect(result.low).toEqual({ reasoningEffort: "low" }) - expect(result.high).toEqual({ reasoningEffort: "high" }) + describe("@ai-sdk/openai-compatible", () => { + test("returns WIDELY_SUPPORTED_EFFORTS with reasoningEffort", () => { + const model = createMockModel({ + id: "custom-provider/custom-model", + providerID: "custom-provider", + api: { + id: "custom-model", + url: "https://api.custom.com", + npm: "@ai-sdk/openai-compatible", + }, }) + const result = ProviderTransform.variants(model) + expect(Object.keys(result)).toEqual(["low", "medium", "high"]) + expect(result.low).toEqual({ reasoningEffort: "low" }) + expect(result.high).toEqual({ reasoningEffort: "high" }) }) + }) - describe("@ai-sdk/deepinfra", () => { - test("returns WIDELY_SUPPORTED_EFFORTS with reasoningEffort", () => { - const model = createMockModel({ - id: "deepinfra/llama-4", - providerID: "deepinfra", - api: { - id: "llama-4-sc", - url: "https://api.deepinfra.com", - npm: "@ai-sdk/deepinfra", - }, - }) - const result = ProviderTransform.variants(model) - expect(Object.keys(result)).toEqual(["low", "medium", "high"]) - expect(result.low).toEqual({ reasoningEffort: "low" }) - expect(result.high).toEqual({ reasoningEffort: "high" }) - }) - }) - - describe("@ai-sdk/openai-compatible", () => { - test("returns WIDELY_SUPPORTED_EFFORTS with reasoningEffort", () => { - const model = createMockModel({ - id: "custom-provider/custom-model", - providerID: "custom-provider", - api: { - id: "custom-model", - url: "https://api.custom.com", - npm: "@ai-sdk/openai-compatible", - }, - }) - const result = ProviderTransform.variants(model) - expect(Object.keys(result)).toEqual(["low", "medium", "high"]) - expect(result.low).toEqual({ reasoningEffort: "low" }) - expect(result.high).toEqual({ reasoningEffort: "high" }) - }) - }) - - describe("@ai-sdk/azure", () => { - test("o1-mini returns empty object", () => { - const model = createMockModel({ + describe("@ai-sdk/azure", () => { + test("o1-mini returns empty object", () => { + const model = createMockModel({ + id: "o1-mini", + providerID: "azure", + api: { id: "o1-mini", - providerID: "azure", - api: { - id: "o1-mini", - url: "https://azure.com", - npm: "@ai-sdk/azure", - }, - }) - const result = ProviderTransform.variants(model) - expect(result).toEqual({}) + url: "https://azure.com", + npm: "@ai-sdk/azure", + }, }) + const result = ProviderTransform.variants(model) + expect(result).toEqual({}) + }) - test("standard azure models return custom efforts with reasoningSummary", () => { - const model = createMockModel({ + test("standard azure models return custom efforts with reasoningSummary", () => { + const model = createMockModel({ + id: "o1", + providerID: "azure", + api: { id: "o1", - providerID: "azure", - api: { - id: "o1", - url: "https://azure.com", - npm: "@ai-sdk/azure", - }, - }) - const result = ProviderTransform.variants(model) - expect(Object.keys(result)).toEqual(["low", "medium", "high"]) - expect(result.low).toEqual({ - reasoningEffort: "low", - reasoningSummary: "auto", - include: ["reasoning.encrypted_content"], - }) + url: "https://azure.com", + npm: "@ai-sdk/azure", + }, }) - - test("gpt-5 adds minimal effort", () => { - const model = createMockModel({ - id: "gpt-5", - providerID: "azure", - api: { - id: "gpt-5", - url: "https://azure.com", - npm: "@ai-sdk/azure", - }, - }) - const result = ProviderTransform.variants(model) - expect(Object.keys(result)).toEqual(["minimal", "low", "medium", "high"]) + const result = ProviderTransform.variants(model) + expect(Object.keys(result)).toEqual(["low", "medium", "high"]) + expect(result.low).toEqual({ + reasoningEffort: "low", + reasoningSummary: "auto", + include: ["reasoning.encrypted_content"], }) }) - describe("@ai-sdk/openai", () => { - test("gpt-5-pro returns empty object", () => { - const model = createMockModel({ + test("gpt-5 adds minimal effort", () => { + const model = createMockModel({ + id: "gpt-5", + providerID: "azure", + api: { + id: "gpt-5", + url: "https://azure.com", + npm: "@ai-sdk/azure", + }, + }) + const result = ProviderTransform.variants(model) + expect(Object.keys(result)).toEqual(["minimal", "low", "medium", "high"]) + }) + }) + + describe("@ai-sdk/openai", () => { + test("gpt-5-pro returns empty object", () => { + const model = createMockModel({ + id: "gpt-5-pro", + providerID: "openai", + api: { id: "gpt-5-pro", - providerID: "openai", - api: { - id: "gpt-5-pro", - url: "https://api.openai.com", - npm: "@ai-sdk/openai", - }, - }) - const result = ProviderTransform.variants(model) - expect(result).toEqual({}) + url: "https://api.openai.com", + npm: "@ai-sdk/openai", + }, }) + const result = ProviderTransform.variants(model) + expect(result).toEqual({}) + }) - test("standard openai models return custom efforts with reasoningSummary", () => { - const model = createMockModel({ + test("standard openai models return custom efforts with reasoningSummary", () => { + const model = createMockModel({ + id: "gpt-5", + providerID: "openai", + api: { id: "gpt-5", - providerID: "openai", - api: { - id: "gpt-5", - url: "https://api.openai.com", - npm: "@ai-sdk/openai", - }, - release_date: "2024-06-01", - }) - const result = ProviderTransform.variants(model) - expect(Object.keys(result)).toEqual(["minimal", "low", "medium", "high"]) - expect(result.low).toEqual({ - reasoningEffort: "low", - reasoningSummary: "auto", - include: ["reasoning.encrypted_content"], - }) + url: "https://api.openai.com", + npm: "@ai-sdk/openai", + }, + release_date: "2024-06-01", }) + const result = ProviderTransform.variants(model) + expect(Object.keys(result)).toEqual(["minimal", "low", "medium", "high"]) + expect(result.low).toEqual({ + reasoningEffort: "low", + reasoningSummary: "auto", + include: ["reasoning.encrypted_content"], + }) + }) - test("models after 2025-11-13 include 'none' effort", () => { - const model = createMockModel({ + test("models after 2025-11-13 include 'none' effort", () => { + const model = createMockModel({ + id: "gpt-5-nano", + providerID: "openai", + api: { id: "gpt-5-nano", - providerID: "openai", - api: { - id: "gpt-5-nano", - url: "https://api.openai.com", - npm: "@ai-sdk/openai", - }, - release_date: "2025-11-14", - }) - const result = ProviderTransform.variants(model) - expect(Object.keys(result)).toEqual(["none", "minimal", "low", "medium", "high"]) + url: "https://api.openai.com", + npm: "@ai-sdk/openai", + }, + release_date: "2025-11-14", }) + const result = ProviderTransform.variants(model) + expect(Object.keys(result)).toEqual(["none", "minimal", "low", "medium", "high"]) + }) - test("models after 2025-12-04 include 'xhigh' effort", () => { - const model = createMockModel({ - id: "openai/gpt-5-chat", - providerID: "openai", - api: { - id: "gpt-5-chat", - url: "https://api.openai.com", - npm: "@ai-sdk/openai", - }, - release_date: "2025-12-05", - }) - const result = ProviderTransform.variants(model) - expect(Object.keys(result)).toEqual(["none", "minimal", "low", "medium", "high", "xhigh"]) + test("models after 2025-12-04 include 'xhigh' effort", () => { + const model = createMockModel({ + id: "openai/gpt-5-chat", + providerID: "openai", + api: { + id: "gpt-5-chat", + url: "https://api.openai.com", + npm: "@ai-sdk/openai", + }, + release_date: "2025-12-05", + }) + const result = ProviderTransform.variants(model) + expect(Object.keys(result)).toEqual(["none", "minimal", "low", "medium", "high", "xhigh"]) + }) + }) + + describe("@ai-sdk/anthropic", () => { + test("sonnet 4.6 returns adaptive thinking options", () => { + const model = createMockModel({ + id: "anthropic/claude-sonnet-4-6", + providerID: "anthropic", + api: { + id: "claude-sonnet-4-6", + url: "https://api.anthropic.com", + npm: "@ai-sdk/anthropic", + }, + }) + const result = ProviderTransform.variants(model) + expect(Object.keys(result)).toEqual(["low", "medium", "high", "max"]) + expect(result.high).toEqual({ + thinking: { + type: "adaptive", + }, + effort: "high", }) }) - describe("@ai-sdk/anthropic", () => { - test("returns high and max with thinking config", () => { - const model = createMockModel({ - id: "anthropic/claude-4", - providerID: "anthropic", - api: { - id: "claude-4", - url: "https://api.anthropic.com", - npm: "@ai-sdk/anthropic", - }, - }) - const result = ProviderTransform.variants(model) - expect(Object.keys(result)).toEqual(["high", "max"]) - expect(result.high).toEqual({ - thinking: { - type: "enabled", - budgetTokens: 16000, - }, - }) - expect(result.max).toEqual({ - thinking: { - type: "enabled", - budgetTokens: 31999, - }, - }) + test("returns high and max with thinking config", () => { + const model = createMockModel({ + id: "anthropic/claude-4", + providerID: "anthropic", + api: { + id: "claude-4", + url: "https://api.anthropic.com", + npm: "@ai-sdk/anthropic", + }, + }) + const result = ProviderTransform.variants(model) + expect(Object.keys(result)).toEqual(["high", "max"]) + expect(result.high).toEqual({ + thinking: { + type: "enabled", + budgetTokens: 16000, + }, + }) + expect(result.max).toEqual({ + thinking: { + type: "enabled", + budgetTokens: 31999, + }, + }) + }) + }) + + describe("@ai-sdk/amazon-bedrock", () => { + test("anthropic sonnet 4.6 returns adaptive reasoning options", () => { + const model = createMockModel({ + id: "bedrock/anthropic-claude-sonnet-4-6", + providerID: "bedrock", + api: { + id: "anthropic.claude-sonnet-4-6", + url: "https://bedrock.amazonaws.com", + npm: "@ai-sdk/amazon-bedrock", + }, + }) + const result = ProviderTransform.variants(model) + expect(Object.keys(result)).toEqual(["low", "medium", "high", "max"]) + expect(result.max).toEqual({ + reasoningConfig: { + type: "adaptive", + maxReasoningEffort: "max", + }, }) }) - describe("@ai-sdk/amazon-bedrock", () => { - test("returns WIDELY_SUPPORTED_EFFORTS with reasoningConfig", () => { - const model = createMockModel({ - id: "bedrock/llama-4", - providerID: "bedrock", - api: { - id: "llama-4-sc", - url: "https://bedrock.amazonaws.com", - npm: "@ai-sdk/amazon-bedrock", - }, - }) - const result = ProviderTransform.variants(model) - expect(Object.keys(result)).toEqual(["low", "medium", "high"]) - expect(result.low).toEqual({ - reasoningConfig: { - type: "enabled", - maxReasoningEffort: "low", - }, - }) + test("returns WIDELY_SUPPORTED_EFFORTS with reasoningConfig", () => { + const model = createMockModel({ + id: "bedrock/llama-4", + providerID: "bedrock", + api: { + id: "llama-4-sc", + url: "https://bedrock.amazonaws.com", + npm: "@ai-sdk/amazon-bedrock", + }, + }) + const result = ProviderTransform.variants(model) + expect(Object.keys(result)).toEqual(["low", "medium", "high"]) + expect(result.low).toEqual({ + reasoningConfig: { + type: "enabled", + maxReasoningEffort: "low", + }, + }) + }) + }) + + describe("@ai-sdk/google", () => { + test("gemini-2.5 returns high and max with thinkingConfig and thinkingBudget", () => { + const model = createMockModel({ + id: "google/gemini-2.5-pro", + providerID: "google", + api: { + id: "gemini-2.5-pro", + url: "https://generativelanguage.googleapis.com", + npm: "@ai-sdk/google", + }, + }) + const result = ProviderTransform.variants(model) + expect(Object.keys(result)).toEqual(["high", "max"]) + expect(result.high).toEqual({ + thinkingConfig: { + includeThoughts: true, + thinkingBudget: 16000, + }, + }) + expect(result.max).toEqual({ + thinkingConfig: { + includeThoughts: true, + thinkingBudget: 24576, + }, }) }) - describe("@ai-sdk/google", () => { - test("gemini-2.5 returns high and max with thinkingConfig and thinkingBudget", () => { - const model = createMockModel({ - id: "google/gemini-2.5-pro", - providerID: "google", - api: { - id: "gemini-2.5-pro", - url: "https://generativelanguage.googleapis.com", - npm: "@ai-sdk/google", - }, - }) - const result = ProviderTransform.variants(model) - expect(Object.keys(result)).toEqual(["high", "max"]) - expect(result.high).toEqual({ - thinkingConfig: { - includeThoughts: true, - thinkingBudget: 16000, - }, - }) - expect(result.max).toEqual({ - thinkingConfig: { - includeThoughts: true, - thinkingBudget: 24576, - }, - }) + test("other gemini models return low and high with thinkingLevel", () => { + const model = createMockModel({ + id: "google/gemini-2.0-pro", + providerID: "google", + api: { + id: "gemini-2.0-pro", + url: "https://generativelanguage.googleapis.com", + npm: "@ai-sdk/google", + }, }) - - test("other gemini models return low and high with thinkingLevel", () => { - const model = createMockModel({ - id: "google/gemini-2.0-pro", - providerID: "google", - api: { - id: "gemini-2.0-pro", - url: "https://generativelanguage.googleapis.com", - npm: "@ai-sdk/google", - }, - }) - const result = ProviderTransform.variants(model) - expect(Object.keys(result)).toEqual(["low", "high"]) - expect(result.low).toEqual({ + const result = ProviderTransform.variants(model) + expect(Object.keys(result)).toEqual(["low", "high"]) + expect(result.low).toEqual({ + thinkingConfig: { includeThoughts: true, thinkingLevel: "low", - }) - expect(result.high).toEqual({ + }, + }) + expect(result.high).toEqual({ + thinkingConfig: { includeThoughts: true, thinkingLevel: "high", - }) + }, }) }) + }) - describe("@ai-sdk/google-vertex", () => { - test("gemini-2.5 returns high and max with thinkingConfig and thinkingBudget", () => { - const model = createMockModel({ - id: "google-vertex/gemini-2.5-pro", - providerID: "google-vertex", - api: { - id: "gemini-2.5-pro", - url: "https://vertexai.googleapis.com", - npm: "@ai-sdk/google-vertex", - }, - }) - const result = ProviderTransform.variants(model) - expect(Object.keys(result)).toEqual(["high", "max"]) + describe("@ai-sdk/google-vertex", () => { + test("gemini-2.5 returns high and max with thinkingConfig and thinkingBudget", () => { + const model = createMockModel({ + id: "google-vertex/gemini-2.5-pro", + providerID: "google-vertex", + api: { + id: "gemini-2.5-pro", + url: "https://vertexai.googleapis.com", + npm: "@ai-sdk/google-vertex", + }, }) + const result = ProviderTransform.variants(model) + expect(Object.keys(result)).toEqual(["high", "max"]) + }) - test("other vertex models return low and high with thinkingLevel", () => { - const model = createMockModel({ - id: "google-vertex/gemini-2.0-pro", - providerID: "google-vertex", - api: { - id: "gemini-2.0-pro", - url: "https://vertexai.googleapis.com", - npm: "@ai-sdk/google-vertex", - }, - }) - const result = ProviderTransform.variants(model) - expect(Object.keys(result)).toEqual(["low", "high"]) + test("other vertex models return low and high with thinkingLevel", () => { + const model = createMockModel({ + id: "google-vertex/gemini-2.0-pro", + providerID: "google-vertex", + api: { + id: "gemini-2.0-pro", + url: "https://vertexai.googleapis.com", + npm: "@ai-sdk/google-vertex", + }, }) + const result = ProviderTransform.variants(model) + expect(Object.keys(result)).toEqual(["low", "high"]) + }) + }) + + describe("@ai-sdk/cohere", () => { + test("returns empty object", () => { + const model = createMockModel({ + id: "cohere/command-r", + providerID: "cohere", + api: { + id: "command-r", + url: "https://api.cohere.com", + npm: "@ai-sdk/cohere", + }, + }) + const result = ProviderTransform.variants(model) + expect(result).toEqual({}) + }) + }) + + describe("@ai-sdk/groq", () => { + test("returns none and WIDELY_SUPPORTED_EFFORTS with thinkingLevel", () => { + const model = createMockModel({ + id: "groq/llama-4", + providerID: "groq", + api: { + id: "llama-4-sc", + url: "https://api.groq.com", + npm: "@ai-sdk/groq", + }, + }) + const result = ProviderTransform.variants(model) + expect(Object.keys(result)).toEqual(["none", "low", "medium", "high"]) + expect(result.none).toEqual({ + reasoningEffort: "none", + }) + expect(result.low).toEqual({ + reasoningEffort: "low", + }) + }) + }) + + describe("@ai-sdk/perplexity", () => { + test("returns empty object", () => { + const model = createMockModel({ + id: "perplexity/sonar-plus", + providerID: "perplexity", + api: { + id: "sonar-plus", + url: "https://api.perplexity.ai", + npm: "@ai-sdk/perplexity", + }, + }) + const result = ProviderTransform.variants(model) + expect(result).toEqual({}) }) })