diff --git a/packages/ai/CHANGELOG.md b/packages/ai/CHANGELOG.md index cf73d98b7..a57f01a99 100644 --- a/packages/ai/CHANGELOG.md +++ b/packages/ai/CHANGELOG.md @@ -6,6 +6,8 @@ - Fixed tool-call argument parsing dropping fields when a string contains an invalid `\u` escape such as `\usepackage` ([#10868](https://github.com/earendil-works/pi/issues/10868)) - Fixed Anthropic requests failing with a 400 when an error tool result contains images; the images are now replaced with a text note ([#10794](https://github.com/earendil-works/pi/issues/10794)) +- Fixed OpenCode Go Gemini models failing with a 404 by moving OpenCode Zen and OpenCode Go to the `opencode.ai/inference` endpoints, and added Google Generative AI support to the OpenCode Go provider +- Fixed OpenCode Go MiniMax M2.7 failing with "Model does not support this protocol" by using the Anthropic Messages API ## [1.2.0] - 2026-10-12 diff --git a/packages/ai/scripts/generate-models.ts b/packages/ai/scripts/generate-models.ts index 9b962ccc6..ad4839e14 100644 --- a/packages/ai/scripts/generate-models.ts +++ b/packages/ai/scripts/generate-models.ts @@ -2159,8 +2159,8 @@ async function loadModelsDevData(): Promise[]> { // - @ai-sdk/google → google-generative-ai // - null/undefined/@ai-sdk/openai-compatible → openai-completions const opencodeVariants = [ - { key: "opencode", provider: "opencode", basePath: "https://opencode.ai/zen" }, - { key: "opencode-go", provider: "opencode-go", basePath: "https://opencode.ai/zen/go" }, + { key: "opencode", provider: "opencode", basePath: "https://opencode.ai/inference" }, + { key: "opencode-go", provider: "opencode-go", basePath: "https://opencode.ai/inference/go" }, ] as const; for (const variant of opencodeVariants) { @@ -2209,16 +2209,9 @@ async function loadModelsDevData(): Promise[]> { // Fix known mismatches between models.dev npm data and actual // OpenCode Go endpoint behaviour. models.dev reports these models - // as @ai-sdk/anthropic, but the OpenCode Go endpoints either don't - // accept Anthropic SDK auth (MiniMax M2.7) or are served through - // the OpenAI-compatible /v1/chat/completions path (Qwen 3.5/3.6). - // Switch them to openai-completions so requests use Bearer auth - // and the standard /v1/chat/completions endpoint. + // as @ai-sdk/anthropic, but OpenCode Go serves them through the + // OpenAI-compatible /v1/chat/completions path. if (variant.provider === "opencode-go") { - if (modelId === "minimax-m2.7") { - api = "openai-completions"; - baseUrl = `${variant.basePath}/v1`; - } if (modelId === "qwen3.5-plus" || modelId === "qwen3.6-plus") { api = "openai-completions"; baseUrl = `${variant.basePath}/v1`; @@ -2619,7 +2612,7 @@ async function loadModelsDevClassifierModels(): Promise[] = [ { @@ -2628,7 +2621,7 @@ const OPENCODE_CLASSIFIER_MODELS: ClassifierModel<"typesafe-system-one">[] = [ name: "Jev 1.13", api: "typesafe-system-one", provider: "opencode", - baseUrl: "https://opencode.ai/zen/v1", + baseUrl: "https://opencode.ai/inference/v1", input: ["text"], cost: { input: 0.042, output: 0, cacheRead: 0, cacheWrite: 0 }, contextWindow: 32000, @@ -2639,7 +2632,7 @@ const OPENCODE_CLASSIFIER_MODELS: ClassifierModel<"typesafe-system-one">[] = [ name: "Jev 1.13 Free", api: "typesafe-system-one", provider: "opencode", - baseUrl: "https://opencode.ai/zen/v1", + baseUrl: "https://opencode.ai/inference/v1", input: ["text"], cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 }, contextWindow: 32000, diff --git a/packages/ai/src/providers/opencode-go.ts b/packages/ai/src/providers/opencode-go.ts index 3c0b58f4b..cd38e810b 100644 --- a/packages/ai/src/providers/opencode-go.ts +++ b/packages/ai/src/providers/opencode-go.ts @@ -1,4 +1,5 @@ import { anthropicMessagesApi } from "../api/anthropic-messages.lazy.ts"; +import { googleGenerativeAIApi } from "../api/google-generative-ai.lazy.ts"; import { openAICompletionsApi } from "../api/openai-completions.lazy.ts"; import { openAIResponsesApi } from "../api/openai-responses.lazy.ts"; import { envApiKeyAuth } from "../auth/helpers.ts"; @@ -6,14 +7,17 @@ import { createProvider, type Provider } from "../models.ts"; import { OPENCODE_GO_MODELS } from "./opencode-go.models.ts"; import { withOpenCodeSessionHeader } from "./opencode-headers.ts"; -export function opencodeGoProvider(): Provider<"anthropic-messages" | "openai-completions" | "openai-responses"> { - return createProvider<"anthropic-messages" | "openai-completions" | "openai-responses">({ +type OpenCodeGoApi = "anthropic-messages" | "google-generative-ai" | "openai-completions" | "openai-responses"; + +export function opencodeGoProvider(): Provider { + return createProvider({ id: "opencode-go", name: "OpenCode Go", auth: { apiKey: envApiKeyAuth("OpenCode API key", ["OPENCODE_API_KEY"]) }, models: Object.values(OPENCODE_GO_MODELS), api: { "anthropic-messages": withOpenCodeSessionHeader(anthropicMessagesApi()), + "google-generative-ai": withOpenCodeSessionHeader(googleGenerativeAIApi()), "openai-completions": withOpenCodeSessionHeader(openAICompletionsApi()), "openai-responses": withOpenCodeSessionHeader(openAIResponsesApi()), }, diff --git a/packages/ai/test/classifier-models.test.ts b/packages/ai/test/classifier-models.test.ts index e57b4a67b..5f0cb2cd5 100644 --- a/packages/ai/test/classifier-models.test.ts +++ b/packages/ai/test/classifier-models.test.ts @@ -130,8 +130,8 @@ describe("Models with classifier models", () => { it.each([ ["vercel-ai-gateway", "typesafe-ai/jev", "https://ai-gateway.vercel.sh/typesafe/v1/systemone"], - ["opencode", "jev-1.13", "https://opencode.ai/zen/v1/systemone"], - ["opencode", "jev-1.13-free", "https://opencode.ai/zen/v1/systemone"], + ["opencode", "jev-1.13", "https://opencode.ai/inference/v1/systemone"], + ["opencode", "jev-1.13-free", "https://opencode.ai/inference/v1/systemone"], ])("routes %s Jev (%s) to its TypeSafe-compatible endpoint", async (provider, id, url) => { const models = builtinModels(); const jev = models.getModelOfType("classifier", provider, id); diff --git a/packages/ai/test/supports-xhigh.test.ts b/packages/ai/test/supports-xhigh.test.ts index 664fe4043..155b2b8d9 100644 --- a/packages/ai/test/supports-xhigh.test.ts +++ b/packages/ai/test/supports-xhigh.test.ts @@ -175,12 +175,6 @@ describe("getSupportedThinkingLevels", () => { expect(getSupportedThinkingLevels(model!)).toEqual(["off", "low", "high", "max"]); }); - it("includes low/high/max plus off for DeepSeek V4 Flash on opencode-go", () => { - const model = getModel("opencode-go", "deepseek-v4-flash"); - expect(model).toBeDefined(); - expect(getSupportedThinkingLevels(model!)).toEqual(["off", "low", "high", "max"]); - }); - it("preserves low/high/max metadata for DeepSeek V4.1 Flash on OpenRouter", () => { const model = getModel("openrouter", "deepseek/deepseek-v4.1-flash"); expect(model).toBeDefined();