mirror of
https://github.com/anomalyco/opencode.git
synced 2026-08-15 17:08:21 -04:00
Compare commits
7 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 8eef79784e | |||
| cf7503687a | |||
| 6f8e1dda15 | |||
| 4dcfd9182c | |||
| d7c0db8cee | |||
| 184da0e42e | |||
| a244d82aba |
@@ -51,6 +51,9 @@ export const Model = Schema.Struct({
|
|||||||
release_date: Schema.String,
|
release_date: Schema.String,
|
||||||
attachment: Schema.Boolean,
|
attachment: Schema.Boolean,
|
||||||
reasoning: Schema.Boolean,
|
reasoning: Schema.Boolean,
|
||||||
|
// models.dev is external metadata and reasoning controls are expected to evolve.
|
||||||
|
// Provider normalization extracts the subset understood by this client.
|
||||||
|
reasoning_options: Schema.optional(Schema.Unknown),
|
||||||
temperature: Schema.Boolean,
|
temperature: Schema.Boolean,
|
||||||
tool_call: Schema.Boolean,
|
tool_call: Schema.Boolean,
|
||||||
interleaved: Schema.optional(
|
interleaved: Schema.optional(
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { describe, expect, beforeAll, beforeEach, afterAll } from "bun:test"
|
import { describe, expect, beforeAll, beforeEach, afterAll, test } from "bun:test"
|
||||||
import { Effect, Layer, Ref } from "effect"
|
import { Effect, Layer, Ref, Schema } from "effect"
|
||||||
import { HttpClient, HttpClientResponse } from "effect/unstable/http"
|
import { HttpClient, HttpClientResponse } from "effect/unstable/http"
|
||||||
import { AppNodeBuilder } from "@opencode-ai/core/effect/app-node-builder"
|
import { AppNodeBuilder } from "@opencode-ai/core/effect/app-node-builder"
|
||||||
import { LayerNodePlatform } from "@opencode-ai/core/effect/app-node-platform"
|
import { LayerNodePlatform } from "@opencode-ai/core/effect/app-node-platform"
|
||||||
@@ -126,6 +126,21 @@ const initialState: MockState = {
|
|||||||
calls: [],
|
calls: [],
|
||||||
}
|
}
|
||||||
|
|
||||||
|
test("models.dev model schema keeps reasoning options permissive", () => {
|
||||||
|
const model = Schema.decodeUnknownSync(ModelsDev.Model)({
|
||||||
|
id: "acme-1",
|
||||||
|
name: "Acme One",
|
||||||
|
release_date: "2026-01-01",
|
||||||
|
attachment: false,
|
||||||
|
reasoning: true,
|
||||||
|
reasoning_options: [{ type: "future_control", value: { nested: true } }, "future-shape"],
|
||||||
|
temperature: true,
|
||||||
|
tool_call: true,
|
||||||
|
limit: { context: 128000, output: 8192 },
|
||||||
|
})
|
||||||
|
expect(model.reasoning_options).toEqual([{ type: "future_control", value: { nested: true } }, "future-shape"])
|
||||||
|
})
|
||||||
|
|
||||||
describe("ModelsDev Service", () => {
|
describe("ModelsDev Service", () => {
|
||||||
it.live("get() returns providers from disk when cache file exists", () =>
|
it.live("get() returns providers from disk when cache file exists", () =>
|
||||||
Effect.gen(function* () {
|
Effect.gen(function* () {
|
||||||
|
|||||||
@@ -303,7 +303,13 @@ export async function CodexAuthPlugin(input: PluginInput, options: CodexAuthPlug
|
|||||||
input: 272_000,
|
input: 272_000,
|
||||||
output: 128_000,
|
output: 128_000,
|
||||||
}
|
}
|
||||||
: model.limit,
|
: model.id.includes("gpt-5.6")
|
||||||
|
? {
|
||||||
|
context: 500_000,
|
||||||
|
input: 372_000,
|
||||||
|
output: 128_000,
|
||||||
|
}
|
||||||
|
: model.limit,
|
||||||
},
|
},
|
||||||
]),
|
]),
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -981,6 +981,21 @@ const ProviderInterleaved = Schema.Union([
|
|||||||
}),
|
}),
|
||||||
])
|
])
|
||||||
|
|
||||||
|
const ProviderReasoningOption = Schema.Union([
|
||||||
|
Schema.Struct({
|
||||||
|
type: Schema.Literal("effort"),
|
||||||
|
values: Schema.Array(Schema.String),
|
||||||
|
}),
|
||||||
|
Schema.Struct({
|
||||||
|
type: Schema.Literal("toggle"),
|
||||||
|
}),
|
||||||
|
Schema.Struct({
|
||||||
|
type: Schema.Literal("budget_tokens"),
|
||||||
|
min: optional(Schema.Finite),
|
||||||
|
max: optional(Schema.Finite),
|
||||||
|
}),
|
||||||
|
])
|
||||||
|
|
||||||
const ProviderCapabilities = Schema.Struct({
|
const ProviderCapabilities = Schema.Struct({
|
||||||
temperature: Schema.Boolean,
|
temperature: Schema.Boolean,
|
||||||
reasoning: Schema.Boolean,
|
reasoning: Schema.Boolean,
|
||||||
@@ -1039,6 +1054,7 @@ export const Model = Schema.Struct({
|
|||||||
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,
|
||||||
|
reasoning_options: optional(Schema.Array(ProviderReasoningOption)),
|
||||||
variants: optional(Schema.Record(Schema.String, Schema.Record(Schema.String, Schema.Any))),
|
variants: optional(Schema.Record(Schema.String, Schema.Record(Schema.String, Schema.Any))),
|
||||||
}).annotate({ identifier: "Model" })
|
}).annotate({ identifier: "Model" })
|
||||||
export type Model = Types.DeepMutable<Schema.Schema.Type<typeof Model>>
|
export type Model = Types.DeepMutable<Schema.Schema.Type<typeof Model>>
|
||||||
@@ -1202,6 +1218,40 @@ function cost(c: ModelsDev.Model["cost"]): Model["cost"] {
|
|||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type ReasoningOption = NonNullable<Model["reasoning_options"]>[number]
|
||||||
|
|
||||||
|
function reasoningOptions(input: unknown): Model["reasoning_options"] {
|
||||||
|
if (!Array.isArray(input)) return []
|
||||||
|
return input.flatMap((option) => {
|
||||||
|
const normalized = normalizeReasoningOption(option)
|
||||||
|
return normalized ? [normalized] : []
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function normalizeReasoningOption(option: unknown): ReasoningOption | undefined {
|
||||||
|
if (!isRecord(option)) return
|
||||||
|
if (option.type === "effort") {
|
||||||
|
if (!Array.isArray(option.values)) return
|
||||||
|
return {
|
||||||
|
type: "effort",
|
||||||
|
// models.dev uses null to mean reasoning can be disabled; expose it as "none".
|
||||||
|
values: option.values.flatMap((value) => {
|
||||||
|
if (value === null) return ["none"]
|
||||||
|
return typeof value === "string" ? [value] : []
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (option.type === "toggle") return { type: "toggle" }
|
||||||
|
if (option.type !== "budget_tokens") return
|
||||||
|
const min = typeof option.min === "number" && Number.isFinite(option.min) ? option.min : undefined
|
||||||
|
const max = typeof option.max === "number" && Number.isFinite(option.max) ? option.max : undefined
|
||||||
|
return {
|
||||||
|
type: "budget_tokens",
|
||||||
|
...(min === undefined ? {} : { min }),
|
||||||
|
...(max === undefined ? {} : { max }),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function fromModelsDevModel(provider: ModelsDev.Provider, model: ModelsDev.Model): Model {
|
function fromModelsDevModel(provider: ModelsDev.Provider, model: ModelsDev.Model): Model {
|
||||||
const base: Model = {
|
const base: Model = {
|
||||||
id: ModelV2.ID.make(model.id),
|
id: ModelV2.ID.make(model.id),
|
||||||
@@ -1244,6 +1294,7 @@ function fromModelsDevModel(provider: ModelsDev.Provider, model: ModelsDev.Model
|
|||||||
interleaved: model.interleaved ?? false,
|
interleaved: model.interleaved ?? false,
|
||||||
},
|
},
|
||||||
release_date: model.release_date ?? "",
|
release_date: model.release_date ?? "",
|
||||||
|
reasoning_options: reasoningOptions(model.reasoning_options),
|
||||||
variants: {},
|
variants: {},
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1490,6 +1541,7 @@ const layer = Layer.effect(
|
|||||||
headers: mergeDeep(existingModel?.headers ?? {}, model.headers ?? {}),
|
headers: mergeDeep(existingModel?.headers ?? {}, model.headers ?? {}),
|
||||||
family: model.family ?? existingModel?.family ?? "",
|
family: model.family ?? existingModel?.family ?? "",
|
||||||
release_date: model.release_date ?? existingModel?.release_date ?? "",
|
release_date: model.release_date ?? existingModel?.release_date ?? "",
|
||||||
|
reasoning_options: existingModel?.reasoning_options,
|
||||||
variants: {},
|
variants: {},
|
||||||
}
|
}
|
||||||
const merged = mergeDeep(ProviderTransform.variants(parsedModel), model.variants ?? {})
|
const merged = mergeDeep(ProviderTransform.variants(parsedModel), model.variants ?? {})
|
||||||
|
|||||||
@@ -149,6 +149,30 @@ describe("plugin.codex", () => {
|
|||||||
await enabled.dispose?.()
|
await enabled.dispose?.()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
test("uses Codex context limits for OAuth GPT models", async () => {
|
||||||
|
const hooks = await CodexAuthPlugin({} as never)
|
||||||
|
const limit = { context: 1_050_000, input: 922_000, output: 128_000 }
|
||||||
|
const provider = {
|
||||||
|
models: Object.fromEntries(
|
||||||
|
["gpt-5.4", "gpt-5.5", "gpt-5.6-sol", "gpt-5.6-terra", "gpt-5.6-luna"].map((id) => [
|
||||||
|
id,
|
||||||
|
{ id, api: { id }, limit, cost: {} },
|
||||||
|
]),
|
||||||
|
),
|
||||||
|
}
|
||||||
|
|
||||||
|
const models = await hooks.provider!.models!(provider as never, { auth: { type: "oauth" } } as never)
|
||||||
|
|
||||||
|
expect(models["gpt-5.4"]?.limit).toEqual(limit)
|
||||||
|
expect(models["gpt-5.5"]?.limit).toEqual({ context: 400_000, input: 272_000, output: 128_000 })
|
||||||
|
expect(models["gpt-5.6-sol"]?.limit).toEqual({ context: 500_000, input: 372_000, output: 128_000 })
|
||||||
|
expect(models["gpt-5.6-terra"]?.limit).toEqual({ context: 500_000, input: 372_000, output: 128_000 })
|
||||||
|
expect(models["gpt-5.6-luna"]?.limit).toEqual({ context: 500_000, input: 372_000, output: 128_000 })
|
||||||
|
expect(await hooks.provider!.models!(provider as never, { auth: { type: "api" } } as never)).toBe(
|
||||||
|
provider.models as never,
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
test("deduplicates concurrent Codex token refreshes", async () => {
|
test("deduplicates concurrent Codex token refreshes", async () => {
|
||||||
let auth = {
|
let auth = {
|
||||||
type: "oauth" as const,
|
type: "oauth" as const,
|
||||||
|
|||||||
@@ -159,10 +159,10 @@ it.instance(
|
|||||||
const providers = yield* list
|
const providers = yield* list
|
||||||
expect(providers[ProviderV2.ID.anthropic]).toBeDefined()
|
expect(providers[ProviderV2.ID.anthropic]).toBeDefined()
|
||||||
const models = Object.keys(providers[ProviderV2.ID.anthropic].models)
|
const models = Object.keys(providers[ProviderV2.ID.anthropic].models)
|
||||||
expect(models).toContain("claude-sonnet-4-20250514")
|
expect(models).toContain("claude-sonnet-4-6")
|
||||||
expect(models.length).toBe(1)
|
expect(models.length).toBe(1)
|
||||||
}),
|
}),
|
||||||
{ config: { provider: { anthropic: { whitelist: ["claude-sonnet-4-20250514"] } } } },
|
{ config: { provider: { anthropic: { whitelist: ["claude-sonnet-4-6"] } } } },
|
||||||
)
|
)
|
||||||
|
|
||||||
it.instance(
|
it.instance(
|
||||||
@@ -301,10 +301,10 @@ it.instance("getModel returns model for valid provider/model", () =>
|
|||||||
Effect.gen(function* () {
|
Effect.gen(function* () {
|
||||||
yield* setProcessEnv("ANTHROPIC_API_KEY", "test-api-key")
|
yield* setProcessEnv("ANTHROPIC_API_KEY", "test-api-key")
|
||||||
const provider = yield* Provider.Service
|
const provider = yield* Provider.Service
|
||||||
const model = yield* provider.getModel(ProviderV2.ID.anthropic, ModelV2.ID.make("claude-sonnet-4-20250514"))
|
const model = yield* provider.getModel(ProviderV2.ID.anthropic, ModelV2.ID.make("claude-sonnet-4-6"))
|
||||||
expect(model).toBeDefined()
|
expect(model).toBeDefined()
|
||||||
expect(String(model.providerID)).toBe("anthropic")
|
expect(String(model.providerID)).toBe("anthropic")
|
||||||
expect(String(model.id)).toBe("claude-sonnet-4-20250514")
|
expect(String(model.id)).toBe("claude-sonnet-4-6")
|
||||||
const language = yield* provider.getLanguage(model)
|
const language = yield* provider.getLanguage(model)
|
||||||
expect(language).toBeDefined()
|
expect(language).toBeDefined()
|
||||||
}),
|
}),
|
||||||
@@ -435,7 +435,7 @@ it.instance(
|
|||||||
"model options are merged from existing model",
|
"model options are merged from existing model",
|
||||||
Effect.gen(function* () {
|
Effect.gen(function* () {
|
||||||
const providers = yield* list
|
const providers = yield* list
|
||||||
const model = providers[ProviderV2.ID.anthropic].models["claude-sonnet-4-20250514"]
|
const model = providers[ProviderV2.ID.anthropic].models["claude-sonnet-4-6"]
|
||||||
expect(model.options.customOption).toBe("custom-value")
|
expect(model.options.customOption).toBe("custom-value")
|
||||||
}),
|
}),
|
||||||
{
|
{
|
||||||
@@ -443,7 +443,7 @@ it.instance(
|
|||||||
provider: {
|
provider: {
|
||||||
anthropic: {
|
anthropic: {
|
||||||
options: { apiKey: "test-api-key" },
|
options: { apiKey: "test-api-key" },
|
||||||
models: { "claude-sonnet-4-20250514": { options: { customOption: "custom-value" } } },
|
models: { "claude-sonnet-4-6": { options: { customOption: "custom-value" } } },
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -549,7 +549,7 @@ it.instance(
|
|||||||
Effect.gen(function* () {
|
Effect.gen(function* () {
|
||||||
yield* set("ANTHROPIC_API_KEY", "test-api-key")
|
yield* set("ANTHROPIC_API_KEY", "test-api-key")
|
||||||
const providers = yield* list
|
const providers = yield* list
|
||||||
const model = providers[ProviderV2.ID.anthropic].models["claude-sonnet-4-20250514"]
|
const model = providers[ProviderV2.ID.anthropic].models["claude-sonnet-4-6"]
|
||||||
expect(model.name).toBe("Custom Name for Sonnet")
|
expect(model.name).toBe("Custom Name for Sonnet")
|
||||||
expect(model.capabilities.toolcall).toBe(true)
|
expect(model.capabilities.toolcall).toBe(true)
|
||||||
expect(model.capabilities.attachment).toBe(true)
|
expect(model.capabilities.attachment).toBe(true)
|
||||||
@@ -557,7 +557,7 @@ it.instance(
|
|||||||
}),
|
}),
|
||||||
{
|
{
|
||||||
config: {
|
config: {
|
||||||
provider: { anthropic: { models: { "claude-sonnet-4-20250514": { name: "Custom Name for Sonnet" } } } },
|
provider: { anthropic: { models: { "claude-sonnet-4-6": { name: "Custom Name for Sonnet" } } } },
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
@@ -590,16 +590,16 @@ it.instance(
|
|||||||
const providers = yield* list
|
const providers = yield* list
|
||||||
expect(providers[ProviderV2.ID.anthropic]).toBeDefined()
|
expect(providers[ProviderV2.ID.anthropic]).toBeDefined()
|
||||||
const models = Object.keys(providers[ProviderV2.ID.anthropic].models)
|
const models = Object.keys(providers[ProviderV2.ID.anthropic].models)
|
||||||
expect(models).toContain("claude-sonnet-4-20250514")
|
expect(models).toContain("claude-sonnet-4-6")
|
||||||
expect(models).not.toContain("claude-opus-4-20250514")
|
expect(models).not.toContain("claude-opus-4-6")
|
||||||
expect(models.length).toBe(1)
|
expect(models.length).toBe(1)
|
||||||
}),
|
}),
|
||||||
{
|
{
|
||||||
config: {
|
config: {
|
||||||
provider: {
|
provider: {
|
||||||
anthropic: {
|
anthropic: {
|
||||||
whitelist: ["claude-sonnet-4-20250514", "claude-opus-4-20250514"],
|
whitelist: ["claude-sonnet-4-6", "claude-opus-4-6"],
|
||||||
blacklist: ["claude-opus-4-20250514"],
|
blacklist: ["claude-opus-4-6"],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -773,9 +773,9 @@ it.instance(
|
|||||||
const model = yield* Provider.use.getSmallModel(ProviderV2.ID.anthropic)
|
const model = yield* Provider.use.getSmallModel(ProviderV2.ID.anthropic)
|
||||||
expect(model).toBeDefined()
|
expect(model).toBeDefined()
|
||||||
expect(String(model?.providerID)).toBe("anthropic")
|
expect(String(model?.providerID)).toBe("anthropic")
|
||||||
expect(String(model?.id)).toBe("claude-sonnet-4-20250514")
|
expect(String(model?.id)).toBe("claude-sonnet-4-6")
|
||||||
}),
|
}),
|
||||||
{ config: { small_model: "anthropic/claude-sonnet-4-20250514" } },
|
{ config: { small_model: "anthropic/claude-sonnet-4-6" } },
|
||||||
)
|
)
|
||||||
|
|
||||||
it.instance(
|
it.instance(
|
||||||
@@ -1094,8 +1094,8 @@ it.instance(
|
|||||||
it.instance("getModel returns consistent results", () =>
|
it.instance("getModel returns consistent results", () =>
|
||||||
Effect.gen(function* () {
|
Effect.gen(function* () {
|
||||||
yield* set("ANTHROPIC_API_KEY", "test-api-key")
|
yield* set("ANTHROPIC_API_KEY", "test-api-key")
|
||||||
const model1 = yield* Provider.use.getModel(ProviderV2.ID.anthropic, ModelV2.ID.make("claude-sonnet-4-20250514"))
|
const model1 = yield* Provider.use.getModel(ProviderV2.ID.anthropic, ModelV2.ID.make("claude-sonnet-4-6"))
|
||||||
const model2 = yield* Provider.use.getModel(ProviderV2.ID.anthropic, ModelV2.ID.make("claude-sonnet-4-20250514"))
|
const model2 = yield* Provider.use.getModel(ProviderV2.ID.anthropic, ModelV2.ID.make("claude-sonnet-4-6"))
|
||||||
expect(model1.providerID).toEqual(model2.providerID)
|
expect(model1.providerID).toEqual(model2.providerID)
|
||||||
expect(model1.id).toEqual(model2.id)
|
expect(model1.id).toEqual(model2.id)
|
||||||
expect(model1).toEqual(model2)
|
expect(model1).toEqual(model2)
|
||||||
@@ -1426,6 +1426,54 @@ test("models.dev normalization fills required response fields", () => {
|
|||||||
expect(model.release_date).toBe("")
|
expect(model.release_date).toBe("")
|
||||||
})
|
})
|
||||||
|
|
||||||
|
test("models.dev reasoning options normalize to known shapes", () => {
|
||||||
|
const provider = Provider.fromModelsDevProvider({
|
||||||
|
id: "openai",
|
||||||
|
name: "OpenAI",
|
||||||
|
env: [],
|
||||||
|
npm: "@ai-sdk/openai",
|
||||||
|
models: {
|
||||||
|
reasoner: {
|
||||||
|
id: "reasoner",
|
||||||
|
name: "Reasoner",
|
||||||
|
reasoning: true,
|
||||||
|
reasoning_options: [
|
||||||
|
{ type: "future_control", value: true },
|
||||||
|
{ type: "effort", values: [null, "high", 42] },
|
||||||
|
{ type: "toggle" },
|
||||||
|
{ type: "budget_tokens", min: 1024, max: "invalid" },
|
||||||
|
],
|
||||||
|
limit: { context: 128_000, output: 16_000 },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
} as unknown as ModelsDev.Provider)
|
||||||
|
|
||||||
|
expect(provider.models.reasoner.reasoning_options).toEqual([
|
||||||
|
{ type: "effort", values: ["none", "high"] },
|
||||||
|
{ type: "toggle" },
|
||||||
|
{ type: "budget_tokens", min: 1024 },
|
||||||
|
])
|
||||||
|
})
|
||||||
|
|
||||||
|
test("models.dev models without reasoning options normalize to an empty list", () => {
|
||||||
|
const provider = Provider.fromModelsDevProvider({
|
||||||
|
id: "openai",
|
||||||
|
name: "OpenAI",
|
||||||
|
env: [],
|
||||||
|
npm: "@ai-sdk/openai",
|
||||||
|
models: {
|
||||||
|
reasoner: {
|
||||||
|
id: "gpt-5.4",
|
||||||
|
name: "Reasoner",
|
||||||
|
reasoning: true,
|
||||||
|
limit: { context: 128_000, output: 16_000 },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
} as unknown as ModelsDev.Provider)
|
||||||
|
|
||||||
|
expect(provider.models.reasoner.reasoning_options).toEqual([])
|
||||||
|
})
|
||||||
|
|
||||||
test("public provider info omits invalid models", () => {
|
test("public provider info omits invalid models", () => {
|
||||||
const provider = Provider.fromModelsDevProvider({
|
const provider = Provider.fromModelsDevProvider({
|
||||||
id: "test",
|
id: "test",
|
||||||
@@ -1457,7 +1505,7 @@ it.instance("model variants are generated for reasoning models", () =>
|
|||||||
yield* set("ANTHROPIC_API_KEY", "test-api-key")
|
yield* set("ANTHROPIC_API_KEY", "test-api-key")
|
||||||
const providers = yield* list
|
const providers = yield* list
|
||||||
// Claude sonnet 4 has reasoning capability
|
// Claude sonnet 4 has reasoning capability
|
||||||
const model = providers[ProviderV2.ID.anthropic].models["claude-sonnet-4-20250514"]
|
const model = providers[ProviderV2.ID.anthropic].models["claude-sonnet-4-6"]
|
||||||
expect(model.capabilities.reasoning).toBe(true)
|
expect(model.capabilities.reasoning).toBe(true)
|
||||||
expect(model.variants).toBeDefined()
|
expect(model.variants).toBeDefined()
|
||||||
expect(Object.keys(model.variants!).length).toBeGreaterThan(0)
|
expect(Object.keys(model.variants!).length).toBeGreaterThan(0)
|
||||||
@@ -1469,7 +1517,7 @@ it.instance(
|
|||||||
Effect.gen(function* () {
|
Effect.gen(function* () {
|
||||||
yield* set("ANTHROPIC_API_KEY", "test-api-key")
|
yield* set("ANTHROPIC_API_KEY", "test-api-key")
|
||||||
const providers = yield* list
|
const providers = yield* list
|
||||||
const model = providers[ProviderV2.ID.anthropic].models["claude-sonnet-4-20250514"]
|
const model = providers[ProviderV2.ID.anthropic].models["claude-sonnet-4-6"]
|
||||||
expect(model.variants).toBeDefined()
|
expect(model.variants).toBeDefined()
|
||||||
expect(model.variants!["high"]).toBeUndefined()
|
expect(model.variants!["high"]).toBeUndefined()
|
||||||
// max variant should still exist
|
// max variant should still exist
|
||||||
@@ -1479,7 +1527,7 @@ it.instance(
|
|||||||
config: {
|
config: {
|
||||||
provider: {
|
provider: {
|
||||||
anthropic: {
|
anthropic: {
|
||||||
models: { "claude-sonnet-4-20250514": { variants: { high: { disabled: true } } } },
|
models: { "claude-sonnet-4-6": { variants: { high: { disabled: true } } } },
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -1491,7 +1539,7 @@ it.instance(
|
|||||||
Effect.gen(function* () {
|
Effect.gen(function* () {
|
||||||
yield* set("ANTHROPIC_API_KEY", "test-api-key")
|
yield* set("ANTHROPIC_API_KEY", "test-api-key")
|
||||||
const providers = yield* list
|
const providers = yield* list
|
||||||
const model = providers[ProviderV2.ID.anthropic].models["claude-sonnet-4-20250514"]
|
const model = providers[ProviderV2.ID.anthropic].models["claude-sonnet-4-6"]
|
||||||
expect(model.variants!["high"]).toBeDefined()
|
expect(model.variants!["high"]).toBeDefined()
|
||||||
expect(model.variants!["high"].thinking.budgetTokens).toBe(20000)
|
expect(model.variants!["high"].thinking.budgetTokens).toBe(20000)
|
||||||
}),
|
}),
|
||||||
@@ -1500,7 +1548,7 @@ it.instance(
|
|||||||
provider: {
|
provider: {
|
||||||
anthropic: {
|
anthropic: {
|
||||||
models: {
|
models: {
|
||||||
"claude-sonnet-4-20250514": {
|
"claude-sonnet-4-6": {
|
||||||
variants: { high: { thinking: { type: "enabled", budgetTokens: 20000 } } },
|
variants: { high: { thinking: { type: "enabled", budgetTokens: 20000 } } },
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -1515,7 +1563,7 @@ it.instance(
|
|||||||
Effect.gen(function* () {
|
Effect.gen(function* () {
|
||||||
yield* set("ANTHROPIC_API_KEY", "test-api-key")
|
yield* set("ANTHROPIC_API_KEY", "test-api-key")
|
||||||
const providers = yield* list
|
const providers = yield* list
|
||||||
const model = providers[ProviderV2.ID.anthropic].models["claude-sonnet-4-20250514"]
|
const model = providers[ProviderV2.ID.anthropic].models["claude-sonnet-4-6"]
|
||||||
expect(model.variants!["max"]).toBeDefined()
|
expect(model.variants!["max"]).toBeDefined()
|
||||||
expect(model.variants!["max"].disabled).toBeUndefined()
|
expect(model.variants!["max"].disabled).toBeUndefined()
|
||||||
expect(model.variants!["max"].customField).toBe("test")
|
expect(model.variants!["max"].customField).toBe("test")
|
||||||
@@ -1525,7 +1573,7 @@ it.instance(
|
|||||||
provider: {
|
provider: {
|
||||||
anthropic: {
|
anthropic: {
|
||||||
models: {
|
models: {
|
||||||
"claude-sonnet-4-20250514": {
|
"claude-sonnet-4-6": {
|
||||||
variants: { max: { disabled: false, customField: "test" } },
|
variants: { max: { disabled: false, customField: "test" } },
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -1540,7 +1588,7 @@ it.instance(
|
|||||||
Effect.gen(function* () {
|
Effect.gen(function* () {
|
||||||
yield* set("ANTHROPIC_API_KEY", "test-api-key")
|
yield* set("ANTHROPIC_API_KEY", "test-api-key")
|
||||||
const providers = yield* list
|
const providers = yield* list
|
||||||
const model = providers[ProviderV2.ID.anthropic].models["claude-sonnet-4-20250514"]
|
const model = providers[ProviderV2.ID.anthropic].models["claude-sonnet-4-6"]
|
||||||
expect(model.variants).toBeDefined()
|
expect(model.variants).toBeDefined()
|
||||||
expect(Object.keys(model.variants!).length).toBe(0)
|
expect(Object.keys(model.variants!).length).toBe(0)
|
||||||
}),
|
}),
|
||||||
@@ -1549,8 +1597,13 @@ it.instance(
|
|||||||
provider: {
|
provider: {
|
||||||
anthropic: {
|
anthropic: {
|
||||||
models: {
|
models: {
|
||||||
"claude-sonnet-4-20250514": {
|
"claude-sonnet-4-6": {
|
||||||
variants: { high: { disabled: true }, max: { disabled: true } },
|
variants: {
|
||||||
|
low: { disabled: true },
|
||||||
|
medium: { disabled: true },
|
||||||
|
high: { disabled: true },
|
||||||
|
max: { disabled: true },
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -1564,7 +1617,7 @@ it.instance(
|
|||||||
Effect.gen(function* () {
|
Effect.gen(function* () {
|
||||||
yield* set("ANTHROPIC_API_KEY", "test-api-key")
|
yield* set("ANTHROPIC_API_KEY", "test-api-key")
|
||||||
const providers = yield* list
|
const providers = yield* list
|
||||||
const model = providers[ProviderV2.ID.anthropic].models["claude-sonnet-4-20250514"]
|
const model = providers[ProviderV2.ID.anthropic].models["claude-sonnet-4-6"]
|
||||||
expect(model.variants!["high"]).toBeDefined()
|
expect(model.variants!["high"]).toBeDefined()
|
||||||
// Should have both the generated thinking config and the custom option
|
// Should have both the generated thinking config and the custom option
|
||||||
expect(model.variants!["high"].thinking).toBeDefined()
|
expect(model.variants!["high"].thinking).toBeDefined()
|
||||||
@@ -1575,7 +1628,7 @@ it.instance(
|
|||||||
provider: {
|
provider: {
|
||||||
anthropic: {
|
anthropic: {
|
||||||
models: {
|
models: {
|
||||||
"claude-sonnet-4-20250514": { variants: { high: { extraOption: "custom-value" } } },
|
"claude-sonnet-4-6": { variants: { high: { extraOption: "custom-value" } } },
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||
+165932
-110539
File diff suppressed because it is too large
Load Diff
@@ -2100,6 +2100,20 @@ export type Model = {
|
|||||||
[key: string]: string
|
[key: string]: string
|
||||||
}
|
}
|
||||||
release_date: string
|
release_date: string
|
||||||
|
reasoning_options?: Array<
|
||||||
|
| {
|
||||||
|
type: "effort"
|
||||||
|
values: Array<string>
|
||||||
|
}
|
||||||
|
| {
|
||||||
|
type: "toggle"
|
||||||
|
}
|
||||||
|
| {
|
||||||
|
type: "budget_tokens"
|
||||||
|
min?: number
|
||||||
|
max?: number
|
||||||
|
}
|
||||||
|
>
|
||||||
variants?: {
|
variants?: {
|
||||||
[key: string]: {
|
[key: string]: {
|
||||||
[key: string]: unknown
|
[key: string]: unknown
|
||||||
|
|||||||
@@ -21777,6 +21777,58 @@
|
|||||||
"release_date": {
|
"release_date": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
|
"reasoning_options": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"anyOf": [
|
||||||
|
{
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"type": {
|
||||||
|
"type": "string",
|
||||||
|
"enum": ["effort"]
|
||||||
|
},
|
||||||
|
"values": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": ["type", "values"],
|
||||||
|
"additionalProperties": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"type": {
|
||||||
|
"type": "string",
|
||||||
|
"enum": ["toggle"]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": ["type"],
|
||||||
|
"additionalProperties": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"type": {
|
||||||
|
"type": "string",
|
||||||
|
"enum": ["budget_tokens"]
|
||||||
|
},
|
||||||
|
"min": {
|
||||||
|
"type": "number"
|
||||||
|
},
|
||||||
|
"max": {
|
||||||
|
"type": "number"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": ["type"],
|
||||||
|
"additionalProperties": false
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
"variants": {
|
"variants": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"additionalProperties": {
|
"additionalProperties": {
|
||||||
|
|||||||
Reference in New Issue
Block a user