Ver Fonte

fix(core): derive reasoning variants from models.dev

Dax Raad há 1 mês atrás
pai
commit
6ca6566bd3

+ 28 - 7
packages/core/src/plugin/models-dev.ts

@@ -38,12 +38,33 @@ function cost(input: ModelsDev.Model["cost"]) {
   ]
 }
 
-function variants(model: ModelsDev.Model) {
-  return Object.entries(model.experimental?.modes ?? {}).map(([id, item]) => ({
-    id: ModelV2.VariantID.make(id),
-    headers: { ...(item.provider?.headers ?? {}) },
-    body: { ...(item.provider?.body ?? {}) },
-  }))
+function variants(model: ModelsDev.Model, packageName: string | undefined): ModelV2Info["variants"] {
+  const result = new Map<ModelV2.VariantID, ModelV2Info["variants"][number]>()
+  if (packageName === "@ai-sdk/openai" || packageName === "@ai-sdk/openai-compatible") {
+    const option = model.reasoning_options?.find((option) => option.type === "effort")
+    for (const value of option?.values ?? []) {
+      const id = value === null ? "none" : value
+      if (typeof id !== "string") continue
+      const variantID = ModelV2.VariantID.make(id)
+      result.set(variantID, {
+        id: variantID,
+        headers: {},
+        body:
+          packageName === "@ai-sdk/openai"
+            ? { include: ["reasoning.encrypted_content"], reasoning: { effort: id, summary: "auto" } }
+            : { reasoning_effort: id },
+      })
+    }
+  }
+  for (const [id, item] of Object.entries(model.experimental?.modes ?? {})) {
+    const variantID = ModelV2.VariantID.make(id)
+    result.set(variantID, {
+      id: variantID,
+      headers: { ...(item.provider?.headers ?? {}) },
+      body: { ...(item.provider?.body ?? {}) },
+    })
+  }
+  return [...result.values()]
 }
 
 function mergeVariants(model: ModelV2Info, next: ModelV2Info["variants"]) {
@@ -121,7 +142,7 @@ export const ModelsDevPlugin = define({
                 input: [...(model.modalities?.input ?? [])],
                 output: [...(model.modalities?.output ?? [])],
               }
-              mergeVariants(draft, variants(model))
+              mergeVariants(draft, variants(model, model.provider?.npm ?? item.npm))
               draft.time.released = released(model.release_date)
               draft.cost = cost(model.cost)
               draft.status = model.status ?? "active"

+ 16 - 17
packages/core/test/plugin/fixtures/models-dev.json

@@ -9,26 +9,25 @@
     "id": "local",
     "name": "Local",
     "env": [],
+    "models": {}
+  },
+  "opencode": {
+    "id": "opencode",
+    "name": "OpenCode",
+    "env": [],
+    "npm": "@ai-sdk/openai-compatible",
     "models": {
-      "model": {
-        "id": "model",
-        "name": "Local Model",
-        "release_date": "2026-01-01",
-        "attachment": false,
+      "gpt-5.5": {
+        "id": "gpt-5.5",
+        "name": "GPT-5.5",
+        "release_date": "2026-04-23",
+        "attachment": true,
         "reasoning": true,
-        "temperature": true,
+        "reasoning_options": [{ "type": "effort", "values": [null, "none", "low", "medium", "high", "xhigh"] }],
+        "temperature": false,
         "tool_call": true,
-        "limit": { "context": 1000, "output": 100 },
-        "experimental": {
-          "modes": {
-            "high": {
-              "provider": { "body": { "reasoning_effort": "high" } }
-            },
-            "max": {
-              "provider": { "headers": { "x-variant": "max" }, "body": { "reasoning_effort": "max" } }
-            }
-          }
-        }
+        "limit": { "context": 400000, "output": 128000 },
+        "provider": { "npm": "@ai-sdk/openai", "api": "https://console.opencode.ai/inference/openai/v1" }
       }
     }
   }

+ 46 - 11
packages/core/test/plugin/models-dev.test.ts

@@ -76,7 +76,7 @@ describe("ModelsDevPlugin", () => {
     ),
   )
 
-  it.effect("loads models.dev variants without replacing existing variants", () =>
+  it.effect("derives OpenAI reasoning variants from models.dev reasoning options", () =>
     Effect.acquireUseRelease(
       Effect.sync(() => {
         const previous = {
@@ -89,27 +89,61 @@ describe("ModelsDevPlugin", () => {
       }),
       () =>
         Effect.gen(function* () {
-          const service = yield* Catalog.Service
+          const catalog = yield* Catalog.Service
           const integrations = yield* Integration.Service
-          yield* service.transform((catalog) => {
-            catalog.model.update(ProviderV2.ID.make("local"), ModelV2.ID.make("model"), (model) => {
+          yield* catalog.transform((catalog) => {
+            catalog.model.update(ProviderV2.ID.opencode, ModelV2.ID.make("gpt-5.5"), (model) => {
               model.variants = [
-                { id: ModelV2.VariantID.make("high"), headers: { custom: "true" }, body: {} },
-                { id: ModelV2.VariantID.make("custom"), headers: {}, body: { custom: true } },
+                {
+                  id: ModelV2.VariantID.make("high"),
+                  headers: { custom: "true" },
+                  body: { custom: true },
+                },
               ]
             })
           })
           yield* ModelsDevPlugin.effect(
             host({
-              catalog: catalogHost(service),
+              catalog: catalogHost(catalog),
               integration: integrationHost(integrations),
             }),
           )
 
-          expect((yield* service.model.get(ProviderV2.ID.make("local"), ModelV2.ID.make("model")))?.variants).toEqual([
-            expect.objectContaining({ id: "high", headers: { custom: "true" } }),
-            expect.objectContaining({ id: "max", headers: { "x-variant": "max" }, body: { reasoning_effort: "max" } }),
-            expect.objectContaining({ id: "custom", body: { custom: true } }),
+          expect((yield* catalog.model.get(ProviderV2.ID.opencode, ModelV2.ID.make("gpt-5.5")))?.variants).toEqual([
+            {
+              id: ModelV2.VariantID.make("none"),
+              headers: {},
+              body: {
+                include: ["reasoning.encrypted_content"],
+                reasoning: { effort: "none", summary: "auto" },
+              },
+            },
+            expect.objectContaining({
+              id: "low",
+              body: {
+                include: ["reasoning.encrypted_content"],
+                reasoning: { effort: "low", summary: "auto" },
+              },
+            }),
+            expect.objectContaining({
+              id: "medium",
+              body: {
+                include: ["reasoning.encrypted_content"],
+                reasoning: { effort: "medium", summary: "auto" },
+              },
+            }),
+            expect.objectContaining({
+              id: "high",
+              headers: { custom: "true" },
+              body: { custom: true },
+            }),
+            expect.objectContaining({
+              id: "xhigh",
+              body: {
+                include: ["reasoning.encrypted_content"],
+                reasoning: { effort: "xhigh", summary: "auto" },
+              },
+            }),
           ])
         }).pipe(Effect.provide(ModelsDev.defaultLayer)),
       (previous) =>
@@ -119,4 +153,5 @@ describe("ModelsDevPlugin", () => {
         }),
     ),
   )
+
 })