Преглед изворни кода

refactor(provider): share model status schema (#26595)

Co-authored-by: Developer <temp@example.com>
Kit Langton пре 4 месеци
родитељ
комит
6849f96825

+ 2 - 1
packages/opencode/src/config/provider.ts

@@ -1,6 +1,7 @@
 import { Schema } from "effect"
 import { Schema } from "effect"
 import { zod } from "@opencode-ai/core/effect-zod"
 import { zod } from "@opencode-ai/core/effect-zod"
 import { PositiveInt, withStatics } from "@opencode-ai/core/schema"
 import { PositiveInt, withStatics } from "@opencode-ai/core/schema"
+import { ModelStatus } from "@/provider/model-status"
 
 
 export const Model = Schema.Struct({
 export const Model = Schema.Struct({
   id: Schema.optional(Schema.String),
   id: Schema.optional(Schema.String),
@@ -49,7 +50,7 @@ export const Model = Schema.Struct({
     }),
     }),
   ),
   ),
   experimental: Schema.optional(Schema.Boolean),
   experimental: Schema.optional(Schema.Boolean),
-  status: Schema.optional(Schema.Literals(["alpha", "beta", "deprecated", "active"])),
+  status: Schema.optional(ModelStatus),
   provider: Schema.optional(
   provider: Schema.optional(
     Schema.Struct({ npm: Schema.optional(Schema.String), api: Schema.optional(Schema.String) }),
     Schema.Struct({ npm: Schema.optional(Schema.String), api: Schema.optional(Schema.String) }),
   ),
   ),

+ 9 - 0
packages/opencode/src/provider/model-status.ts

@@ -0,0 +1,9 @@
+import { Schema } from "effect"
+
+export const CatalogModelStatus = Schema.Literals(["alpha", "beta", "deprecated"])
+export type CatalogModelStatus = typeof CatalogModelStatus.Type
+
+export const ModelStatus = Schema.Literals(["alpha", "beta", "deprecated", "active"])
+export type ModelStatus = typeof ModelStatus.Type
+
+export * as ProviderModelStatus from "./model-status"

+ 2 - 1
packages/opencode/src/provider/models.ts

@@ -8,6 +8,7 @@ import { Flock } from "@opencode-ai/core/util/flock"
 import { Hash } from "@opencode-ai/core/util/hash"
 import { Hash } from "@opencode-ai/core/util/hash"
 import { AppFileSystem } from "@opencode-ai/core/filesystem"
 import { AppFileSystem } from "@opencode-ai/core/filesystem"
 import { withTransientReadRetry } from "@/util/effect-http-client"
 import { withTransientReadRetry } from "@/util/effect-http-client"
+import { CatalogModelStatus } from "./model-status"
 
 
 const Cost = Schema.Struct({
 const Cost = Schema.Struct({
   input: Schema.Finite,
   input: Schema.Finite,
@@ -71,7 +72,7 @@ export const Model = Schema.Struct({
       ),
       ),
     }),
     }),
   ),
   ),
-  status: Schema.optional(Schema.Literals(["alpha", "beta", "deprecated", "active"])),
+  status: Schema.optional(CatalogModelStatus),
   provider: Schema.optional(
   provider: Schema.optional(
     Schema.Struct({ npm: Schema.optional(Schema.String), api: Schema.optional(Schema.String) }),
     Schema.Struct({ npm: Schema.optional(Schema.String), api: Schema.optional(Schema.String) }),
   ),
   ),

+ 2 - 1
packages/opencode/src/provider/provider.ts

@@ -28,6 +28,7 @@ import { optionalOmitUndefined, withStatics } from "@opencode-ai/core/schema"
 
 
 import * as ProviderTransform from "./transform"
 import * as ProviderTransform from "./transform"
 import { ModelID, ProviderID } from "./schema"
 import { ModelID, ProviderID } from "./schema"
+import { ModelStatus } from "./model-status"
 
 
 const log = Log.create({ service: "provider" })
 const log = Log.create({ service: "provider" })
 
 
@@ -897,7 +898,7 @@ export const Model = Schema.Struct({
   capabilities: ProviderCapabilities,
   capabilities: ProviderCapabilities,
   cost: ProviderCost,
   cost: ProviderCost,
   limit: ProviderLimit,
   limit: ProviderLimit,
-  status: Schema.Literals(["alpha", "beta", "deprecated", "active"]),
+  status: ModelStatus,
   options: Schema.Record(Schema.String, Schema.Any),
   options: Schema.Record(Schema.String, Schema.Any),
   headers: Schema.Record(Schema.String, Schema.String),
   headers: Schema.Record(Schema.String, Schema.String),
   release_date: Schema.String,
   release_date: Schema.String,

+ 2 - 1
packages/opencode/src/v2/model.ts

@@ -1,4 +1,5 @@
 import { withStatics } from "@opencode-ai/core/schema"
 import { withStatics } from "@opencode-ai/core/schema"
+import { ModelStatus } from "@/provider/model-status"
 import { Array, Context, Effect, HashMap, Layer, Option, Order, pipe, Schema } from "effect"
 import { Array, Context, Effect, HashMap, Layer, Option, Order, pipe, Schema } from "effect"
 import { DateTimeUtcFromMillis } from "effect/Schema"
 import { DateTimeUtcFromMillis } from "effect/Schema"
 
 
@@ -114,7 +115,7 @@ export class Info extends Schema.Class<Info>("Model.Info")({
     released: DateTimeUtcFromMillis,
     released: DateTimeUtcFromMillis,
   }),
   }),
   cost: Cost.pipe(Schema.Array),
   cost: Cost.pipe(Schema.Array),
-  status: Schema.Literals(["alpha", "beta", "deprecated", "active"]),
+  status: ModelStatus,
   limit: Schema.Struct({
   limit: Schema.Struct({
     context: Schema.Int,
     context: Schema.Int,
     input: Schema.Int.pipe(Schema.optional),
     input: Schema.Int.pipe(Schema.optional),

+ 61 - 0
packages/opencode/test/provider/model-status.test.ts

@@ -0,0 +1,61 @@
+import { describe, expect, test } from "bun:test"
+import { Schema } from "effect"
+import { ConfigProvider } from "@/config/provider"
+import { CatalogModelStatus, ModelStatus } from "@/provider/model-status"
+import { ModelsDev } from "@/provider/models"
+import { Provider } from "@/provider/provider"
+
+describe("provider model status schemas", () => {
+  test("keeps catalog status separate from normalized provider status", () => {
+    expect(Schema.decodeUnknownSync(CatalogModelStatus)("deprecated")).toBe("deprecated")
+    expect(() => Schema.decodeUnknownSync(CatalogModelStatus)("active")).toThrow()
+    expect(Schema.decodeUnknownSync(ModelStatus)("active")).toBe("active")
+  })
+
+  test("accepts active status across public provider schemas", () => {
+    expect(Schema.decodeUnknownSync(ConfigProvider.Model)({ status: "active" }).status).toBe("active")
+    expect(
+      Schema.decodeUnknownSync(ModelsDev.Model)({
+        id: "test-model",
+        name: "Test Model",
+        release_date: "2026-01-01",
+        attachment: false,
+        reasoning: false,
+        temperature: true,
+        tool_call: true,
+        limit: { context: 128000, output: 8192 },
+      }).status,
+    ).toBeUndefined()
+    expect(
+      Schema.decodeUnknownSync(Provider.Model)({
+        id: "test-model",
+        providerID: "test-provider",
+        api: {
+          id: "test-model",
+          url: "",
+          npm: "@ai-sdk/openai-compatible",
+        },
+        name: "Test Model",
+        capabilities: {
+          temperature: true,
+          reasoning: false,
+          attachment: false,
+          toolcall: true,
+          input: { text: true, audio: false, image: false, video: false, pdf: false },
+          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: 128000, output: 8192 },
+        status: "active",
+        options: {},
+        headers: {},
+        release_date: "2026-01-01",
+      }).status,
+    ).toBe("active")
+  })
+})