| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218 |
- import path from "path"
- import { NodeFileSystem } from "@effect/platform-node"
- import { LayerNode } from "@kirincode-ai/core/effect/layer-node"
- import { FSUtil } from "@kirincode-ai/core/fs-util"
- import { describe, expect, test } from "bun:test"
- import { Effect, FileSystem, Layer } from "effect"
- import { Global } from "@kirincode-ai/core/global"
- import {
- createVariantRuntime,
- cycleVariant,
- formatModelLabel,
- pickVariant,
- resolveVariant,
- } from "@/cli/cmd/run/variant.shared"
- import type { SessionMessages } from "@/cli/cmd/run/session.shared"
- import type { RunProvider } from "@/cli/cmd/run/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",
- },
- },
- },
- ]
- function userMessage(
- id: string,
- input: { providerID: string; modelID: string; variant?: string },
- ): SessionMessages[number] {
- return {
- info: {
- id,
- sessionID: "session-1",
- role: "user",
- time: {
- created: 1,
- },
- agent: "build",
- model: input,
- },
- parts: [],
- }
- }
- const it = testEffect(Layer.mergeAll(LayerNode.compile(FSUtil.node), NodeFileSystem.layer))
- 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(LayerNode.compile(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 raw session messages", () => {
- const msgs: SessionMessages = [
- userMessage("msg-1", { providerID: "openai", modelID: "gpt-5", variant: "high" }),
- userMessage("msg-2", { providerID: "anthropic", modelID: "sonnet", variant: "max" }),
- userMessage("msg-3", { providerID: "openai", modelID: "gpt-5", variant: "minimal" }),
- ]
- expect(pickVariant(model, msgs)).toBe("minimal")
- })
- it.live("reads and writes saved variants through a runtime-backed app fs layer", () =>
- Effect.gen(function* () {
- const filesys = yield* FileSystem.FileSystem
- const fs = yield* FSUtil.Service
- const root = yield* filesys.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(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 filesys = yield* FileSystem.FileSystem
- const fs = yield* FSUtil.Service
- const root = yield* filesys.makeTempDirectoryScoped()
- const file = path.join(root, "model.json")
- yield* filesys.writeFileString(file, "{")
- const svc = createVariantRuntime(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",
- },
- })
- }),
- )
- })
|