Jelajahi Sumber

fix(core): route Vertex Anthropic natively (#41836)

Aiden Cline 5 hari lalu
induk
melakukan
ab6e18e4ff

+ 21 - 0
packages/core/src/aisdk-native.ts

@@ -51,6 +51,27 @@ export function map(input: MapInput): Mapping | undefined {
           ...mapGoogleOptions(input.settings),
         },
       }
+    case "@ai-sdk/google-vertex/anthropic":
+      return {
+        package: "@opencode-ai/ai/providers/google-vertex/messages",
+        settings: {
+          ...baseSettings,
+          ...(typeof input.settings.accessToken === "string" ? { accessToken: input.settings.accessToken } : {}),
+          ...(typeof input.settings.location === "string" ? { location: input.settings.location } : {}),
+          ...(typeof input.settings.project === "string" ? { project: input.settings.project } : {}),
+          ...(isRecord(input.settings.thinking) || typeof input.settings.effort === "string"
+            ? {
+                providerOptions: {
+                  anthropic: {
+                    ...(isRecord(input.settings.thinking) ? { thinking: input.settings.thinking } : {}),
+                    ...(typeof input.settings.effort === "string" ? { effort: input.settings.effort } : {}),
+                  },
+                },
+              }
+            : {}),
+        },
+        ...(isStringRecord(input.settings.headers) ? { headers: input.settings.headers } : {}),
+      }
     case "@openrouter/ai-sdk-provider":
       return mapOpenRouter(input.settings, baseSettings)
     case "@ai-sdk/xai":

+ 29 - 0
packages/core/test/aisdk-native.test.ts

@@ -273,6 +273,35 @@ describe("AISDKNative", () => {
     })
   })
 
+  test("maps Vertex Anthropic settings to native Messages", () => {
+    expect(
+      map("@ai-sdk/google-vertex/anthropic", {
+        accessToken: "vertex-token",
+        baseURL: "https://vertex.example/v1",
+        headers: { "x-test": "value" },
+        location: "eu",
+        project: "vertex-project",
+        thinking: { type: "adaptive", display: "summarized" },
+        effort: "high",
+      }),
+    ).toEqual({
+      package: "@opencode-ai/ai/providers/google-vertex/messages",
+      settings: {
+        accessToken: "vertex-token",
+        baseURL: "https://vertex.example/v1",
+        location: "eu",
+        project: "vertex-project",
+        providerOptions: {
+          anthropic: {
+            thinking: { type: "adaptive", display: "summarized" },
+            effort: "high",
+          },
+        },
+      },
+      headers: { "x-test": "value" },
+    })
+  })
+
   test("maps supported xAI settings", () => {
     expect(
       map("@ai-sdk/xai", {

+ 51 - 0
packages/core/test/model-resolver.test.ts

@@ -763,6 +763,57 @@ describe("ModelResolver", () => {
     }),
   )
 
+  it.effect("routes Vertex Anthropic catalog models through native Messages", () =>
+    Effect.gen(function* () {
+      const native = yield* ModelResolver.fromCatalogModel(model(Provider.aisdk("@ai-sdk/openai")))
+      const credential = Credential.OAuth.make({
+        type: "oauth",
+        methodID: Integration.MethodID.make("device"),
+        access: "vertex-token",
+        refresh: "refresh",
+        expires: Date.now() + 60_000,
+      })
+
+      const resolved = yield* ModelResolver.fromCatalogModel(
+        model(Provider.aisdk("@ai-sdk/google-vertex/anthropic"), {
+          modelID: "claude-sonnet-4-6",
+          settings: {
+            location: "eu",
+            project: "vertex-project",
+            thinking: { type: "adaptive", display: "summarized" },
+            effort: "high",
+          },
+        }),
+        credential,
+        {
+          loadPackage: (specifier) => {
+            expect(specifier).toBe("@opencode-ai/ai/providers/google-vertex/messages")
+            return Effect.succeed({
+              model: (modelID, settings) => {
+                expect(modelID).toBe("claude-sonnet-4-6")
+                expect(settings).toMatchObject({
+                  accessToken: "vertex-token",
+                  location: "eu",
+                  project: "vertex-project",
+                  providerOptions: {
+                    anthropic: {
+                      thinking: { type: "adaptive", display: "summarized" },
+                      effort: "high",
+                    },
+                  },
+                })
+                return LanguageModel.make({ id: modelID, provider: "native-provider", route: native.route })
+              },
+            })
+          },
+          loadAISDK: () => Effect.die("AI SDK loader should not be called"),
+        },
+      )
+
+      expect(resolved).toMatchObject({ id: "claude-sonnet-4-6", provider: "test-provider" })
+    }),
+  )
+
   it.effect("merges mapped OpenRouter headers and body with catalog overlays", () =>
     ModelResolver.fromCatalogModel(
       model(Provider.aisdk("@openrouter/ai-sdk-provider"), {