Files
2026-07-13 18:38:25 -05:00

200 lines
5.9 KiB
TypeScript

import path from "path"
import { AppNodeBuilder } from "@opencode-ai/core/effect/app-node-builder"
import { FSUtil } from "@opencode-ai/core/fs-util"
import { describe, expect, test } from "bun:test"
import { Effect, Layer } from "effect"
import { Global } from "@opencode-ai/core/global"
import {
createVariantRuntime,
cycleVariant,
formatModelLabel,
pickVariant,
resolveVariant,
} from "@opencode-ai/cli/mini/variant.shared"
import type { RunSession } from "@opencode-ai/cli/mini/session.shared"
import type { RunProvider } from "@opencode-ai/cli/mini/types"
import { testEffect } from "../../lib/effect"
const model = {
providerID: "openai",
modelID: "gpt-5",
}
const providers: RunProvider[] = [
{
id: "openai",
name: "OpenAI",
source: "api",
env: [],
options: {},
models: {
"gpt-5": {
id: "gpt-5",
providerID: "openai",
api: {
id: "gpt-5",
url: "https://openai.test",
npm: "@ai-sdk/openai",
},
name: "GPT-5",
capabilities: {
temperature: true,
reasoning: true,
attachment: true,
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",
},
},
},
]
const it = testEffect(AppNodeBuilder.build(FSUtil.node))
function remap(root: string, file: string) {
if (file === Global.Path.state) {
return root
}
if (file.startsWith(Global.Path.state + path.sep)) {
return path.join(root, path.relative(Global.Path.state, file))
}
return file
}
function remappedFs(root: string) {
return Layer.effect(
FSUtil.Service,
Effect.gen(function* () {
const fs = yield* FSUtil.Service
return FSUtil.Service.of({
...fs,
readJson: (file) => fs.readJson(remap(root, file)),
writeJson: (file, data, mode) => fs.writeJson(remap(root, file), data, mode),
})
}),
).pipe(Layer.provide(AppNodeBuilder.build(FSUtil.node)))
}
describe("run variant shared", () => {
test("prefers cli then session then saved variants", () => {
expect(resolveVariant("max", "high", "low", ["low", "high"])).toBe("max")
expect(resolveVariant(undefined, "high", "low", ["low", "high"])).toBe("high")
expect(resolveVariant(undefined, "missing", "low", ["low", "high"])).toBe("low")
})
test("cycles through variants and back to default", () => {
expect(cycleVariant(undefined, ["low", "high"])).toBe("low")
expect(cycleVariant("low", ["low", "high"])).toBe("high")
expect(cycleVariant("high", ["low", "high"])).toBeUndefined()
expect(cycleVariant(undefined, [])).toBeUndefined()
})
test("formats model labels", () => {
expect(formatModelLabel(model, undefined)).toBe("gpt-5 · openai")
expect(formatModelLabel(model, "high")).toBe("gpt-5 · openai · high")
expect(formatModelLabel(model, undefined, providers)).toBe("GPT-5 · OpenAI")
expect(formatModelLabel(model, "high", providers)).toBe("GPT-5 · OpenAI · high")
})
test("picks the latest matching variant from session history", () => {
const session: RunSession = {
first: false,
turns: [
{ prompt: { text: "one", parts: [] }, provider: "openai", model: "gpt-5", variant: "high" },
{ prompt: { text: "two", parts: [] }, provider: "anthropic", model: "sonnet", variant: "max" },
{ prompt: { text: "three", parts: [] }, provider: "openai", model: "gpt-5", variant: "minimal" },
],
}
expect(pickVariant(model, session)).toBe("minimal")
})
it.live("reads and writes saved variants through a runtime-backed app fs layer", () =>
Effect.gen(function* () {
const fs = yield* FSUtil.Service
const root = yield* fs.makeTempDirectoryScoped()
const file = path.join(root, "model.json")
yield* fs.writeJson(file, {
recent: [{ providerID: "anthropic", modelID: "sonnet" }],
variant: {
"openai/gpt-4.1": "low",
},
})
const svc = createVariantRuntime([[FSUtil.node, remappedFs(root)]])
yield* Effect.promise(() => svc.saveVariant(model, "high"))
expect(yield* Effect.promise(() => svc.resolveSavedVariant(model))).toBe("high")
expect(yield* fs.readJson(file)).toEqual({
recent: [{ providerID: "anthropic", modelID: "sonnet" }],
variant: {
"openai/gpt-4.1": "low",
"openai/gpt-5": "high",
},
})
yield* Effect.promise(() => svc.saveVariant(model, undefined))
expect(yield* Effect.promise(() => svc.resolveSavedVariant(model))).toBeUndefined()
expect(yield* fs.readJson(file)).toEqual({
recent: [{ providerID: "anthropic", modelID: "sonnet" }],
variant: {
"openai/gpt-4.1": "low",
},
})
}),
)
it.live("repairs malformed saved variant state on the next write", () =>
Effect.gen(function* () {
const fs = yield* FSUtil.Service
const root = yield* fs.makeTempDirectoryScoped()
const file = path.join(root, "model.json")
yield* fs.writeFileString(file, "{")
const svc = createVariantRuntime([[FSUtil.node, remappedFs(root)]])
yield* Effect.promise(() => svc.saveVariant(model, "high"))
expect(yield* Effect.promise(() => svc.resolveSavedVariant(model))).toBe("high")
expect(yield* fs.readJson(file)).toEqual({
variant: {
"openai/gpt-5": "high",
},
})
}),
)
})