Compare commits
2 commits
dev
...
refactor-p
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d421260038 | ||
|
|
3aeb870e71 |
10 changed files with 112 additions and 8 deletions
|
|
@ -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"])),
|
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) }),
|
||||||
),
|
),
|
||||||
|
|
|
||||||
6
packages/opencode/src/provider/model-status.ts
Normal file
6
packages/opencode/src/provider/model-status.ts
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
import { Schema } from "effect"
|
||||||
|
|
||||||
|
export const ModelStatus = Schema.Literals(["alpha", "beta", "deprecated", "active"])
|
||||||
|
export type ModelStatus = typeof ModelStatus.Type
|
||||||
|
|
||||||
|
export * as ProviderModelStatus from "./model-status"
|
||||||
|
|
@ -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 { ModelStatus } 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"])),
|
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) }),
|
||||||
),
|
),
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
|
|
|
||||||
|
|
@ -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),
|
||||||
|
|
|
||||||
57
packages/opencode/test/provider/model-status.test.ts
Normal file
57
packages/opencode/test/provider/model-status.test.ts
Normal file
|
|
@ -0,0 +1,57 @@
|
||||||
|
import { describe, expect, test } from "bun:test"
|
||||||
|
import { Schema } from "effect"
|
||||||
|
import { ConfigProvider } from "@/config/provider"
|
||||||
|
import { ModelStatus } from "@/provider/model-status"
|
||||||
|
import { ModelsDev } from "@/provider/models"
|
||||||
|
import { Provider } from "@/provider/provider"
|
||||||
|
|
||||||
|
describe("provider model status schemas", () => {
|
||||||
|
test("accept active status across config, models.dev, and public provider schemas", () => {
|
||||||
|
expect(Schema.decodeUnknownSync(ModelStatus)("active")).toBe("active")
|
||||||
|
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: "active",
|
||||||
|
}).status,
|
||||||
|
).toBe("active")
|
||||||
|
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")
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
@ -47,4 +47,41 @@ describe("config HttpApi", () => {
|
||||||
lsp: false,
|
lsp: false,
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
test("serves config with active provider model status", async () => {
|
||||||
|
await using tmp = await tmpdir({
|
||||||
|
config: {
|
||||||
|
formatter: false,
|
||||||
|
lsp: false,
|
||||||
|
provider: {
|
||||||
|
omniroute: {
|
||||||
|
models: {
|
||||||
|
"gpt-4o": {
|
||||||
|
status: "active",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
const response = await app().request("/config", {
|
||||||
|
headers: {
|
||||||
|
"x-opencode-directory": tmp.path,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
expect(response.status).toBe(200)
|
||||||
|
expect(await response.json()).toMatchObject({
|
||||||
|
provider: {
|
||||||
|
omniroute: {
|
||||||
|
models: {
|
||||||
|
"gpt-4o": {
|
||||||
|
status: "active",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
|
||||||
|
|
@ -1065,7 +1065,7 @@ export type ProviderConfig = {
|
||||||
output: Array<"text" | "audio" | "image" | "video" | "pdf">
|
output: Array<"text" | "audio" | "image" | "video" | "pdf">
|
||||||
}
|
}
|
||||||
experimental?: boolean
|
experimental?: boolean
|
||||||
status?: "alpha" | "beta" | "deprecated"
|
status?: "alpha" | "beta" | "deprecated" | "active"
|
||||||
options?: {
|
options?: {
|
||||||
[key: string]: unknown
|
[key: string]: unknown
|
||||||
}
|
}
|
||||||
|
|
@ -3012,7 +3012,7 @@ export type ProviderListResponses = {
|
||||||
output: Array<"text" | "audio" | "image" | "video" | "pdf">
|
output: Array<"text" | "audio" | "image" | "video" | "pdf">
|
||||||
}
|
}
|
||||||
experimental?: boolean
|
experimental?: boolean
|
||||||
status?: "alpha" | "beta" | "deprecated"
|
status?: "alpha" | "beta" | "deprecated" | "active"
|
||||||
options: {
|
options: {
|
||||||
[key: string]: unknown
|
[key: string]: unknown
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1060,7 +1060,7 @@ export type ProviderConfig = {
|
||||||
output: Array<"text" | "audio" | "image" | "video" | "pdf">
|
output: Array<"text" | "audio" | "image" | "video" | "pdf">
|
||||||
}
|
}
|
||||||
experimental?: boolean
|
experimental?: boolean
|
||||||
status?: "alpha" | "beta" | "deprecated"
|
status?: "alpha" | "beta" | "deprecated" | "active"
|
||||||
provider?: {
|
provider?: {
|
||||||
npm?: string
|
npm?: string
|
||||||
api?: string
|
api?: string
|
||||||
|
|
|
||||||
|
|
@ -11725,7 +11725,7 @@
|
||||||
},
|
},
|
||||||
"status": {
|
"status": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"enum": ["alpha", "beta", "deprecated"]
|
"enum": ["alpha", "beta", "deprecated", "active"]
|
||||||
},
|
},
|
||||||
"provider": {
|
"provider": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue