Compare commits

..
Author SHA1 Message Date
James Long 2bf2ab3b50 fix(cli): stop setting exit code when run cancels a form 2026-09-28 22:56:37 +00:00
9 changed files with 1 additions and 131 deletions
-1
View File
@@ -164,7 +164,6 @@ export async function runNonInteractivePrompt(input: Input) {
if (!formAlreadySettled(error)) throw error
}
formCancelled = true
if (input.compatibility !== "v1") process.exitCode = 1
}
const consume = async () => {
@@ -2117,7 +2117,6 @@ export type ConfigEntry =
name?: string
env?: Array<string>
package?: string
compatibility?: ModelCompatibility
settings?: ConfigProviderSettings
headers?: { [x: string]: string }
body?: { [x: string]: JsonValue }
@@ -99,13 +99,6 @@ export const Plugin = define({
if (configuredDefault !== undefined) models.default.set(configuredDefault.providerID, configuredDefault.model)
for (const [item, definition] of sources.models) {
const providerID = definition.providerID
if (item.compatibility !== undefined)
for (const model of models.list(providerID)) {
if (Object.hasOwn(item.models ?? {}, model.id)) continue
models.update(providerID, model.id, (draft) => {
draft.compatibility = { ...draft.compatibility, ...item.compatibility }
})
}
for (const [id, config] of Object.entries(item.models ?? {})) {
const source = definition.models.get(id)
const inherit = source?.inherit || !models.get(providerID, id)
@@ -118,8 +111,6 @@ export const Plugin = define({
if (config.family !== undefined) model.family = config.family
if (config.name !== undefined) model.name = config.name
if (config.modelID !== undefined) model.modelID = config.modelID
if (item.compatibility !== undefined)
model.compatibility = { ...model.compatibility, ...item.compatibility }
if (config.compatibility !== undefined)
model.compatibility = { ...model.compatibility, ...config.compatibility }
if (config.package !== undefined) model.package = config.package
-1
View File
@@ -250,7 +250,6 @@ function migrateStandardProvider(info: ConfigProviderV1.Info) {
name: info.name,
env: info.env,
package: info.npm ? Provider.aisdk(info.npm) : undefined,
compatibility: options.compatibility,
settings: info.api ? { ...options.settings, baseURL: info.api } : info.options ? options.settings : undefined,
headers: info.options && options.headers,
body: info.options && options.body,
@@ -1,22 +1,17 @@
export * as ConfigProviderOptionsV1 from "./provider-options.js"
import type { Model } from "@opencode/schema/model"
type Options = Readonly<Record<string, unknown>>
export interface ProviderResult {
readonly settings: Record<string, unknown>
readonly headers?: Record<string, string>
readonly body?: Record<string, unknown>
readonly compatibility?: Model.Compatibility
}
export function provider(options: Options): ProviderResult {
const headers = options.headers
const body = options.body
const settings = Object.fromEntries(
Object.entries(options).filter(([key]) => key !== "headers" && key !== "body" && key !== "setCacheKey"),
)
const settings = Object.fromEntries(Object.entries(options).filter(([key]) => key !== "headers" && key !== "body"))
const headerOverlay =
typeof headers === "object" && headers !== null && !Array.isArray(headers)
? Object.fromEntries(
@@ -28,10 +23,6 @@ export function provider(options: Options): ProviderResult {
settings,
headers: headerOverlay,
body: bodyOverlay,
// V1 `setCacheKey` was a provider-wide switch for sending the prompt cache key; false also disabled the
// built-in defaults for OpenAI and xAI.
compatibility:
typeof options.setCacheKey === "boolean" ? { supportsPromptCacheKey: options.setCacheKey } : undefined,
}
}
-16
View File
@@ -835,22 +835,6 @@ describe("Config", () => {
expect(migrated.providers?.custom?.models?.boolean?.compatibility).toBeUndefined()
})
test("migrates v1 setCacheKey to provider compatibility", () => {
const migrated = ConfigMigrateV1.migrate({
provider: {
enabled: { options: { setCacheKey: true, baseURL: "https://enabled.example/v1" } },
disabled: { options: { setCacheKey: false } },
unset: { options: { baseURL: "https://unset.example/v1" } },
},
})
expect(migrated.providers?.enabled?.compatibility).toEqual({ supportsPromptCacheKey: true })
expect(migrated.providers?.enabled?.settings).toEqual({ baseURL: "https://enabled.example/v1" })
expect(migrated.providers?.disabled?.compatibility).toEqual({ supportsPromptCacheKey: false })
expect(migrated.providers?.disabled?.settings).toEqual({})
expect(migrated.providers?.unset?.compatibility).toBeUndefined()
})
for (const subtask of [true, false]) {
test(`migrates v1 command configuration with subtask: ${subtask}`, () => {
expect(
@@ -24,19 +24,6 @@ describe("ConfigProviderOptionsV1", () => {
})
})
test("lowers setCacheKey to prompt cache key compatibility", () => {
expect(ConfigProviderOptionsV1.provider({ apiKey: "secret", setCacheKey: true })).toEqual({
settings: { apiKey: "secret" },
headers: undefined,
body: undefined,
compatibility: { supportsPromptCacheKey: true },
})
expect(ConfigProviderOptionsV1.provider({ setCacheKey: false }).compatibility).toEqual({
supportsPromptCacheKey: false,
})
expect(ConfigProviderOptionsV1.provider({ apiKey: "secret" }).compatibility).toBeUndefined()
})
test("keeps model and variant options unchanged", () => {
expect(
ConfigProviderOptionsV1.model({
@@ -420,84 +420,6 @@ describe("ConfigProviderPlugin.Plugin", () => {
}),
)
it.effect("applies provider compatibility to every model unless the model overrides it", () =>
Effect.gen(function* () {
const providers = yield* Provider.Service
const models = yield* Model.Service
const providerID = Provider.ID.make("custom")
yield* providers.transform((editor) => {
editor.models.update(providerID, Model.ID.make("catalog"), (model) => {
model.compatibility = { requireFinishReason: true }
})
editor.models.update(Provider.ID.make("other"), Model.ID.make("catalog"), () => {})
})
yield* addPlugin([
new Document({
type: "document",
info: decode({
providers: {
custom: {
compatibility: { supportsPromptCacheKey: true },
models: {
inherited: {},
overridden: { compatibility: { supportsPromptCacheKey: false } },
},
},
},
}),
}),
])
expect((yield* models.get(providerID, Model.ID.make("catalog")))?.compatibility).toEqual({
requireFinishReason: true,
supportsPromptCacheKey: true,
})
expect((yield* models.get(providerID, Model.ID.make("inherited")))?.compatibility).toEqual({
supportsPromptCacheKey: true,
})
expect((yield* models.get(providerID, Model.ID.make("overridden")))?.compatibility).toEqual({
supportsPromptCacheKey: false,
})
expect((yield* models.get(Provider.ID.make("other"), Model.ID.make("catalog")))?.compatibility).toBeUndefined()
}),
)
it.effect("maps migrated v1 setCacheKey onto provider models", () =>
Effect.gen(function* () {
const providers = yield* Provider.Service
const models = yield* Model.Service
yield* providers.transform((editor) => {
editor.models.update(Provider.ID.make("enabled"), Model.ID.make("catalog"), () => {})
editor.models.update(Provider.ID.make("disabled"), Model.ID.make("catalog"), (model) => {
model.compatibility = { supportsPromptCacheKey: true }
})
editor.models.update(Provider.ID.make("unset"), Model.ID.make("catalog"), () => {})
})
const result = ConfigNormalize.normalize({
provider: {
enabled: { options: { setCacheKey: true }, models: { chat: {} } },
disabled: { options: { setCacheKey: false } },
unset: { options: { baseURL: "https://unset.example/v1" } },
},
})
if (result.type !== "normalized") throw new Error("Expected normalized config")
expect(result.diagnostics).toEqual([])
yield* addPlugin([new Document({ type: "document", info: decode(result.encoded) })])
const compatibility = (providerID: string, modelID: string) =>
models
.get(Provider.ID.make(providerID), Model.ID.make(modelID))
.pipe(Effect.map((model) => model?.compatibility?.supportsPromptCacheKey))
expect(yield* compatibility("enabled", "catalog")).toBe(true)
expect(yield* compatibility("enabled", "chat")).toBe(true)
expect(yield* compatibility("disabled", "catalog")).toBe(false)
expect(yield* compatibility("unset", "catalog")).toBeUndefined()
expect((yield* providers.get(Provider.ID.make("enabled")))?.settings).not.toHaveProperty("setCacheKey")
}),
)
it.effect("generates variants after rewriting a configured model package", () =>
Effect.gen(function* () {
const models = yield* Model.Service
-2
View File
@@ -87,8 +87,6 @@ export class Info extends Schema.Class<Info>("Config.Provider")({
name: Schema.String.pipe(optional),
env: Schema.String.pipe(Schema.Array, optional),
package: Schema.String.pipe(optional),
/** Compatibility defaults for every model of this provider. Model-level compatibility takes precedence. */
compatibility: Compatibility.pipe(optional),
...Overlays,
models: Schema.Record(Schema.String, Model).pipe(optional),
}) {}