mirror of
https://github.com/anomalyco/opencode.git
synced 2026-05-01 22:10:23 +00:00
refactor(provider): migrate provider domain to Effect Schema (#24027)
This commit is contained in:
parent
1e439b8226
commit
93940a1859
4 changed files with 93 additions and 113 deletions
|
|
@ -274,9 +274,9 @@ Possible later tightening after the Schema-first migration is stable:
|
||||||
|
|
||||||
### Provider domain
|
### Provider domain
|
||||||
|
|
||||||
- [ ] `src/provider/auth.ts`
|
- [x] `src/provider/auth.ts`
|
||||||
- [ ] `src/provider/models.ts`
|
- [x] `src/provider/models.ts`
|
||||||
- [ ] `src/provider/provider.ts`
|
- [x] `src/provider/provider.ts`
|
||||||
|
|
||||||
### Tool schemas
|
### Tool schemas
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,12 @@
|
||||||
import type { AuthOAuthResult, Hooks } from "@opencode-ai/plugin"
|
import type { AuthOAuthResult, Hooks } from "@opencode-ai/plugin"
|
||||||
import { NamedError } from "@opencode-ai/shared/util/error"
|
|
||||||
import { Auth } from "@/auth"
|
import { Auth } from "@/auth"
|
||||||
import { InstanceState } from "@/effect"
|
import { InstanceState } from "@/effect"
|
||||||
import { zod } from "@/util/effect-zod"
|
import { zod } from "@/util/effect-zod"
|
||||||
|
import { namedSchemaError } from "@/util/named-schema-error"
|
||||||
import { withStatics } from "@/util/schema"
|
import { withStatics } from "@/util/schema"
|
||||||
import { Plugin } from "../plugin"
|
import { Plugin } from "../plugin"
|
||||||
import { ProviderID } from "./schema"
|
import { ProviderID } from "./schema"
|
||||||
import { Array as Arr, Effect, Layer, Record, Result, Context, Schema } from "effect"
|
import { Array as Arr, Effect, Layer, Record, Result, Context, Schema } from "effect"
|
||||||
import z from "zod"
|
|
||||||
|
|
||||||
const When = Schema.Struct({
|
const When = Schema.Struct({
|
||||||
key: Schema.String,
|
key: Schema.String,
|
||||||
|
|
@ -70,22 +69,16 @@ export const CallbackInput = Schema.Struct({
|
||||||
}).pipe(withStatics((s) => ({ zod: zod(s) })))
|
}).pipe(withStatics((s) => ({ zod: zod(s) })))
|
||||||
export type CallbackInput = Schema.Schema.Type<typeof CallbackInput>
|
export type CallbackInput = Schema.Schema.Type<typeof CallbackInput>
|
||||||
|
|
||||||
export const OauthMissing = NamedError.create("ProviderAuthOauthMissing", z.object({ providerID: ProviderID.zod }))
|
export const OauthMissing = namedSchemaError("ProviderAuthOauthMissing", { providerID: ProviderID })
|
||||||
|
|
||||||
export const OauthCodeMissing = NamedError.create(
|
export const OauthCodeMissing = namedSchemaError("ProviderAuthOauthCodeMissing", { providerID: ProviderID })
|
||||||
"ProviderAuthOauthCodeMissing",
|
|
||||||
z.object({ providerID: ProviderID.zod }),
|
|
||||||
)
|
|
||||||
|
|
||||||
export const OauthCallbackFailed = NamedError.create("ProviderAuthOauthCallbackFailed", z.object({}))
|
export const OauthCallbackFailed = namedSchemaError("ProviderAuthOauthCallbackFailed", {})
|
||||||
|
|
||||||
export const ValidationFailed = NamedError.create(
|
export const ValidationFailed = namedSchemaError("ProviderAuthValidationFailed", {
|
||||||
"ProviderAuthValidationFailed",
|
field: Schema.String,
|
||||||
z.object({
|
message: Schema.String,
|
||||||
field: z.string(),
|
})
|
||||||
message: z.string(),
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
|
|
||||||
export type Error =
|
export type Error =
|
||||||
| Auth.AuthError
|
| Auth.AuthError
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
import { Global } from "../global"
|
import { Global } from "../global"
|
||||||
import { Log } from "../util"
|
import { Log } from "../util"
|
||||||
import path from "path"
|
import path from "path"
|
||||||
import z from "zod"
|
import { Schema } from "effect"
|
||||||
import { Installation } from "../installation"
|
import { Installation } from "../installation"
|
||||||
import { Flag } from "../flag/flag"
|
import { Flag } from "../flag/flag"
|
||||||
import { lazy } from "@/util/lazy"
|
import { lazy } from "@/util/lazy"
|
||||||
|
|
@ -21,91 +21,85 @@ const filepath = path.join(
|
||||||
)
|
)
|
||||||
const ttl = 5 * 60 * 1000
|
const ttl = 5 * 60 * 1000
|
||||||
|
|
||||||
type JsonValue = string | number | boolean | null | { [key: string]: JsonValue } | JsonValue[]
|
const Cost = Schema.Struct({
|
||||||
|
input: Schema.Number,
|
||||||
const JsonValue: z.ZodType<JsonValue> = z.lazy(() =>
|
output: Schema.Number,
|
||||||
z.union([z.string(), z.number(), z.boolean(), z.null(), z.array(JsonValue), z.record(z.string(), JsonValue)]),
|
cache_read: Schema.optional(Schema.Number),
|
||||||
)
|
cache_write: Schema.optional(Schema.Number),
|
||||||
|
context_over_200k: Schema.optional(
|
||||||
const Cost = z.object({
|
Schema.Struct({
|
||||||
input: z.number(),
|
input: Schema.Number,
|
||||||
output: z.number(),
|
output: Schema.Number,
|
||||||
cache_read: z.number().optional(),
|
cache_read: Schema.optional(Schema.Number),
|
||||||
cache_write: z.number().optional(),
|
cache_write: Schema.optional(Schema.Number),
|
||||||
context_over_200k: z
|
}),
|
||||||
.object({
|
),
|
||||||
input: z.number(),
|
|
||||||
output: z.number(),
|
|
||||||
cache_read: z.number().optional(),
|
|
||||||
cache_write: z.number().optional(),
|
|
||||||
})
|
|
||||||
.optional(),
|
|
||||||
})
|
})
|
||||||
|
|
||||||
export const Model = z.object({
|
export const Model = Schema.Struct({
|
||||||
id: z.string(),
|
id: Schema.String,
|
||||||
name: z.string(),
|
name: Schema.String,
|
||||||
family: z.string().optional(),
|
family: Schema.optional(Schema.String),
|
||||||
release_date: z.string(),
|
release_date: Schema.String,
|
||||||
attachment: z.boolean(),
|
attachment: Schema.Boolean,
|
||||||
reasoning: z.boolean(),
|
reasoning: Schema.Boolean,
|
||||||
temperature: z.boolean(),
|
temperature: Schema.Boolean,
|
||||||
tool_call: z.boolean(),
|
tool_call: Schema.Boolean,
|
||||||
interleaved: z
|
interleaved: Schema.optional(
|
||||||
.union([
|
Schema.Union([
|
||||||
z.literal(true),
|
Schema.Literal(true),
|
||||||
z
|
Schema.Struct({
|
||||||
.object({
|
field: Schema.Literals(["reasoning_content", "reasoning_details"]),
|
||||||
field: z.enum(["reasoning_content", "reasoning_details"]),
|
}),
|
||||||
})
|
]),
|
||||||
.strict(),
|
),
|
||||||
])
|
cost: Schema.optional(Cost),
|
||||||
.optional(),
|
limit: Schema.Struct({
|
||||||
cost: Cost.optional(),
|
context: Schema.Number,
|
||||||
limit: z.object({
|
input: Schema.optional(Schema.Number),
|
||||||
context: z.number(),
|
output: Schema.Number,
|
||||||
input: z.number().optional(),
|
|
||||||
output: z.number(),
|
|
||||||
}),
|
}),
|
||||||
modalities: z
|
modalities: Schema.optional(
|
||||||
.object({
|
Schema.Struct({
|
||||||
input: z.array(z.enum(["text", "audio", "image", "video", "pdf"])),
|
input: Schema.Array(Schema.Literals(["text", "audio", "image", "video", "pdf"])),
|
||||||
output: z.array(z.enum(["text", "audio", "image", "video", "pdf"])),
|
output: Schema.Array(Schema.Literals(["text", "audio", "image", "video", "pdf"])),
|
||||||
})
|
}),
|
||||||
.optional(),
|
),
|
||||||
experimental: z
|
experimental: Schema.optional(
|
||||||
.object({
|
Schema.Struct({
|
||||||
modes: z
|
modes: Schema.optional(
|
||||||
.record(
|
Schema.Record(
|
||||||
z.string(),
|
Schema.String,
|
||||||
z.object({
|
Schema.Struct({
|
||||||
cost: Cost.optional(),
|
cost: Schema.optional(Cost),
|
||||||
provider: z
|
provider: Schema.optional(
|
||||||
.object({
|
Schema.Struct({
|
||||||
body: z.record(z.string(), JsonValue).optional(),
|
body: Schema.optional(Schema.Record(Schema.String, Schema.MutableJson)),
|
||||||
headers: z.record(z.string(), z.string()).optional(),
|
headers: Schema.optional(Schema.Record(Schema.String, Schema.String)),
|
||||||
})
|
}),
|
||||||
.optional(),
|
),
|
||||||
}),
|
}),
|
||||||
)
|
),
|
||||||
.optional(),
|
),
|
||||||
})
|
}),
|
||||||
.optional(),
|
),
|
||||||
status: z.enum(["alpha", "beta", "deprecated"]).optional(),
|
status: Schema.optional(Schema.Literals(["alpha", "beta", "deprecated"])),
|
||||||
provider: z.object({ npm: z.string().optional(), api: z.string().optional() }).optional(),
|
provider: Schema.optional(
|
||||||
|
Schema.Struct({ npm: Schema.optional(Schema.String), api: Schema.optional(Schema.String) }),
|
||||||
|
),
|
||||||
})
|
})
|
||||||
export type Model = z.infer<typeof Model>
|
export type Model = Schema.Schema.Type<typeof Model>
|
||||||
|
|
||||||
export const Provider = z.object({
|
export const Provider = Schema.Struct({
|
||||||
api: z.string().optional(),
|
api: Schema.optional(Schema.String),
|
||||||
name: z.string(),
|
name: Schema.String,
|
||||||
env: z.array(z.string()),
|
env: Schema.Array(Schema.String),
|
||||||
id: z.string(),
|
id: Schema.String,
|
||||||
npm: z.string().optional(),
|
npm: Schema.optional(Schema.String),
|
||||||
models: z.record(z.string(), Model),
|
models: Schema.Record(Schema.String, Model),
|
||||||
})
|
})
|
||||||
|
|
||||||
export type Provider = z.infer<typeof Provider>
|
export type Provider = Schema.Schema.Type<typeof Provider>
|
||||||
|
|
||||||
function url() {
|
function url() {
|
||||||
return Flag.OPENCODE_MODELS_URL || "https://models.dev"
|
return Flag.OPENCODE_MODELS_URL || "https://models.dev"
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,3 @@
|
||||||
import z from "zod"
|
|
||||||
import os from "os"
|
import os from "os"
|
||||||
import fuzzysort from "fuzzysort"
|
import fuzzysort from "fuzzysort"
|
||||||
import { Config } from "../config"
|
import { Config } from "../config"
|
||||||
|
|
@ -8,7 +7,6 @@ import { Log } from "../util"
|
||||||
import { Npm } from "../npm"
|
import { Npm } from "../npm"
|
||||||
import { Hash } from "@opencode-ai/shared/util/hash"
|
import { Hash } from "@opencode-ai/shared/util/hash"
|
||||||
import { Plugin } from "../plugin"
|
import { Plugin } from "../plugin"
|
||||||
import { NamedError } from "@opencode-ai/shared/util/error"
|
|
||||||
import { type LanguageModelV3 } from "@ai-sdk/provider"
|
import { type LanguageModelV3 } from "@ai-sdk/provider"
|
||||||
import * as ModelsDev from "./models"
|
import * as ModelsDev from "./models"
|
||||||
import { Auth } from "../auth"
|
import { Auth } from "../auth"
|
||||||
|
|
@ -16,6 +14,7 @@ import { Env } from "../env"
|
||||||
import { InstallationVersion } from "../installation/version"
|
import { InstallationVersion } from "../installation/version"
|
||||||
import { Flag } from "../flag/flag"
|
import { Flag } from "../flag/flag"
|
||||||
import { zod } from "@/util/effect-zod"
|
import { zod } from "@/util/effect-zod"
|
||||||
|
import { namedSchemaError } from "@/util/named-schema-error"
|
||||||
import { iife } from "@/util/iife"
|
import { iife } from "@/util/iife"
|
||||||
import { Global } from "../global"
|
import { Global } from "../global"
|
||||||
import path from "path"
|
import path from "path"
|
||||||
|
|
@ -1047,7 +1046,7 @@ export function fromModelsDevProvider(provider: ModelsDev.Provider): Info {
|
||||||
id: ProviderID.make(provider.id),
|
id: ProviderID.make(provider.id),
|
||||||
source: "custom",
|
source: "custom",
|
||||||
name: provider.name,
|
name: provider.name,
|
||||||
env: provider.env ?? [],
|
env: [...(provider.env ?? [])],
|
||||||
options: {},
|
options: {},
|
||||||
models,
|
models,
|
||||||
}
|
}
|
||||||
|
|
@ -1713,18 +1712,12 @@ export function parseModel(model: string) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const ModelNotFoundError = NamedError.create(
|
export const ModelNotFoundError = namedSchemaError("ProviderModelNotFoundError", {
|
||||||
"ProviderModelNotFoundError",
|
providerID: ProviderID,
|
||||||
z.object({
|
modelID: ModelID,
|
||||||
providerID: ProviderID.zod,
|
suggestions: Schema.optional(Schema.Array(Schema.String)),
|
||||||
modelID: ModelID.zod,
|
})
|
||||||
suggestions: z.array(z.string()).optional(),
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
|
|
||||||
export const InitError = NamedError.create(
|
export const InitError = namedSchemaError("ProviderInitError", {
|
||||||
"ProviderInitError",
|
providerID: ProviderID,
|
||||||
z.object({
|
})
|
||||||
providerID: ProviderID.zod,
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue