mirror of
https://github.com/badlogic/pi-mono.git
synced 2026-11-05 23:37:25 +00:00
295 lines
10 KiB
TypeScript
295 lines
10 KiB
TypeScript
import { afterEach, describe, expect, it } from "vitest";
|
|
import type { ExtensionAPI } from "../../src/core/extensions/types.ts";
|
|
import { createAgentSession } from "../../src/core/sdk.ts";
|
|
import { SessionManager } from "../../src/core/session-manager.ts";
|
|
import { createTestResourceLoader } from "../utilities.ts";
|
|
import { createHarness, type Harness } from "./harness.ts";
|
|
|
|
describe("thinking level on model switches", () => {
|
|
const harnesses: Harness[] = [];
|
|
|
|
afterEach(() => {
|
|
while (harnesses.length > 0) harnesses.pop()?.cleanup();
|
|
});
|
|
|
|
it.each([
|
|
{ method: "set", reasoning: true },
|
|
{ method: "set", reasoning: false },
|
|
{ method: "cycle", reasoning: true },
|
|
{ method: "cycle", reasoning: false },
|
|
{ method: "scoped", reasoning: true },
|
|
{ method: "scoped", reasoning: false },
|
|
])("retains the level with $method and target reasoning=$reasoning", async ({ method, reasoning }) => {
|
|
const harness = await createHarness({
|
|
models: [
|
|
{ id: "faux-1", reasoning: true },
|
|
{ id: "faux-2", reasoning },
|
|
],
|
|
settings: { defaultThinkingLevel: "low" },
|
|
});
|
|
harnesses.push(harness);
|
|
const { session } = harness;
|
|
const first = harness.getModel("faux-1")!;
|
|
const second = harness.getModel("faux-2")!;
|
|
if (method === "scoped") session.setScopedModels([{ model: first }, { model: second }]);
|
|
|
|
session.setThinkingLevel("high");
|
|
if (method === "set") await session.setModel(second);
|
|
else await session.cycleModel("forward");
|
|
expect(session.model?.id).toBe(second.id);
|
|
expect(session.thinkingLevel).toBe(reasoning ? "high" : "off");
|
|
|
|
if (method === "set") await session.setModel(first);
|
|
else await session.cycleModel("backward");
|
|
expect(session.model?.id).toBe(first.id);
|
|
expect(session.thinkingLevel).toBe("high");
|
|
expect(harness.settingsManager.getDefaultThinkingLevel()).toBe("low");
|
|
|
|
const expectedLevels = reasoning ? ["high"] : ["high", "off", "high"];
|
|
expect(harness.eventsOfType("thinking_level_changed").map((event) => event.level)).toEqual(expectedLevels);
|
|
expect(
|
|
harness.sessionManager
|
|
.getEntries()
|
|
.filter((entry) => entry.type === "thinking_level_change")
|
|
.map((entry) => entry.thinkingLevel),
|
|
).toEqual(expectedLevels);
|
|
});
|
|
|
|
it("keeps the original value through multiple capability clamps", async () => {
|
|
const harness = await createHarness({
|
|
models: [
|
|
{ id: "faux-1", reasoning: true },
|
|
{ id: "faux-2", reasoning: false },
|
|
{ id: "faux-3", reasoning: true },
|
|
],
|
|
});
|
|
harnesses.push(harness);
|
|
const { session } = harness;
|
|
const first = harness.getModel()!;
|
|
first.thinkingLevelMap = { max: "max" };
|
|
session.setThinkingLevel("max");
|
|
|
|
await session.setModel(harness.getModel("faux-2")!);
|
|
expect(session.thinkingLevel).toBe("off");
|
|
await session.setModel(harness.getModel("faux-3")!);
|
|
expect(session.thinkingLevel).toBe("high");
|
|
await session.setModel(harness.getModel("faux-2")!);
|
|
expect(session.thinkingLevel).toBe("off");
|
|
await session.setModel(first);
|
|
expect(session.thinkingLevel).toBe("max");
|
|
expect(Reflect.get(session, "_thinkingLevelBeforeClamp")).toBeUndefined();
|
|
});
|
|
|
|
it.each([false, true])("remembers a clamped setter value with persist=%s", async (persist) => {
|
|
const harness = await createHarness({
|
|
models: [
|
|
{ id: "faux-1", reasoning: true },
|
|
{ id: "faux-2", reasoning: true },
|
|
],
|
|
settings: { defaultThinkingLevel: "low" },
|
|
});
|
|
harnesses.push(harness);
|
|
const { session } = harness;
|
|
const second = harness.getModel("faux-2")!;
|
|
second.thinkingLevelMap = { max: "max" };
|
|
|
|
session.setThinkingLevel("high");
|
|
session.setThinkingLevel("max", { persist });
|
|
expect(session.thinkingLevel).toBe("high");
|
|
expect(harness.eventsOfType("thinking_level_changed")).toHaveLength(1);
|
|
await session.setModel(second);
|
|
expect(session.thinkingLevel).toBe("max");
|
|
expect(harness.settingsManager.getDefaultThinkingLevel()).toBe(persist ? "max" : "low");
|
|
});
|
|
|
|
it.each(["set", "cycle"])(
|
|
"discards the cached value after a different supported selection via %s",
|
|
async (method) => {
|
|
const harness = await createHarness({
|
|
models: [
|
|
{ id: "faux-1", reasoning: true },
|
|
{ id: "faux-2", reasoning: true },
|
|
],
|
|
});
|
|
harnesses.push(harness);
|
|
const { session } = harness;
|
|
const first = harness.getModel()!;
|
|
first.thinkingLevelMap = { max: "max" };
|
|
session.setThinkingLevel("max");
|
|
await session.setModel(harness.getModel("faux-2")!);
|
|
expect(session.thinkingLevel).toBe("high");
|
|
|
|
if (method === "set") session.setThinkingLevel("low");
|
|
else expect(session.cycleThinkingLevel()).toBe("off");
|
|
await session.setModel(first);
|
|
expect(session.thinkingLevel).toBe(method === "set" ? "low" : "off");
|
|
},
|
|
);
|
|
|
|
it.each([
|
|
{ source: "session", persist: false },
|
|
{ source: "session", persist: true },
|
|
{ source: "extension", persist: false },
|
|
])("same-value $source setter preserves the clamp cache with persist=$persist", async ({ source, persist }) => {
|
|
let extensionApi: ExtensionAPI | undefined;
|
|
const harness = await createHarness({
|
|
models: [
|
|
{ id: "faux-1", reasoning: true },
|
|
{ id: "faux-2", reasoning: false },
|
|
],
|
|
settings: { defaultThinkingLevel: "medium" },
|
|
extensionFactories: [
|
|
(pi) => {
|
|
extensionApi = pi;
|
|
},
|
|
],
|
|
});
|
|
harnesses.push(harness);
|
|
const { session } = harness;
|
|
session.setThinkingLevel("high");
|
|
await session.setModel(harness.getModel("faux-2")!);
|
|
if (source === "session") session.setThinkingLevel("off", { persist });
|
|
else {
|
|
if (!extensionApi) throw new Error("Expected extension API");
|
|
extensionApi.setThinkingLevel("off");
|
|
}
|
|
expect(harness.eventsOfType("thinking_level_changed")).toHaveLength(2);
|
|
await session.setModel(harness.getModel("faux-1")!);
|
|
expect(session.thinkingLevel).toBe("high");
|
|
expect(harness.settingsManager.getDefaultThinkingLevel()).toBe(persist ? "off" : "medium");
|
|
});
|
|
|
|
it.each([
|
|
{ source: "per-model", level: "low" },
|
|
{ source: "per-model", level: "off" },
|
|
{ source: "scoped", level: "low" },
|
|
{ source: "scoped", level: "off" },
|
|
] as const)("$source override $level replaces the cached level", async ({ source, level }) => {
|
|
const harness = await createHarness({
|
|
models: [
|
|
{ id: "faux-1", reasoning: true },
|
|
{ id: "faux-2", reasoning: false },
|
|
{ id: "faux-3", reasoning: true },
|
|
],
|
|
settings: {
|
|
defaultThinkingLevel: "medium",
|
|
modelThinkingLevels: { "faux/faux-3": source === "scoped" ? "minimal" : level },
|
|
},
|
|
});
|
|
harnesses.push(harness);
|
|
const { session } = harness;
|
|
const first = harness.getModel()!;
|
|
const second = harness.getModel("faux-2")!;
|
|
const third = harness.getModel("faux-3")!;
|
|
if (source === "scoped") {
|
|
session.setScopedModels([{ model: first }, { model: second }, { model: third, thinkingLevel: level }]);
|
|
}
|
|
session.setThinkingLevel("high");
|
|
await session.setModel(second);
|
|
expect(session.thinkingLevel).toBe("off");
|
|
if (source === "scoped") await session.cycleModel();
|
|
else await session.setModel(third);
|
|
expect(session.thinkingLevel).toBe(level);
|
|
await session.setModel(first);
|
|
expect(session.thinkingLevel).toBe(level);
|
|
expect(harness.settingsManager.getDefaultThinkingLevel()).toBe("medium");
|
|
});
|
|
|
|
it("caches the model override itself when that override is clamped", async () => {
|
|
const harness = await createHarness({
|
|
models: [
|
|
{ id: "faux-1", reasoning: true },
|
|
{ id: "faux-2", reasoning: false },
|
|
],
|
|
});
|
|
harnesses.push(harness);
|
|
const { session } = harness;
|
|
const second = harness.getModel("faux-2")!;
|
|
session.setThinkingLevel("high");
|
|
await session.setModel(second);
|
|
harness.settingsManager.setModelThinkingLevel("faux", second.id, "low");
|
|
await session.setModel(second);
|
|
expect(session.thinkingLevel).toBe("off");
|
|
await session.setModel(harness.getModel()!);
|
|
expect(session.thinkingLevel).toBe("low");
|
|
});
|
|
|
|
it.each([
|
|
{ source: "explicit", reasoning: false },
|
|
{ source: "explicit", reasoning: true },
|
|
{ source: "global", reasoning: false },
|
|
{ source: "global", reasoning: true },
|
|
{ source: "per-model", reasoning: false },
|
|
{ source: "per-model", reasoning: true },
|
|
{ source: "built-in", reasoning: false },
|
|
{ source: "built-in", reasoning: true },
|
|
])("inherits the effective $source startup level with reasoning=$reasoning", async ({ source, reasoning }) => {
|
|
const harness = await createHarness({
|
|
models: [
|
|
{ id: "faux-1", reasoning },
|
|
{ id: "faux-2", reasoning: true },
|
|
],
|
|
settings: {
|
|
defaultThinkingLevel: source === "built-in" ? undefined : "max",
|
|
modelThinkingLevels: source === "per-model" ? { "faux/faux-1": "low" } : undefined,
|
|
},
|
|
});
|
|
harnesses.push(harness);
|
|
const second = harness.getModel("faux-2")!;
|
|
second.thinkingLevelMap = { max: "max" };
|
|
const { session } = await createAgentSession({
|
|
cwd: harness.tempDir,
|
|
model: harness.getModel(),
|
|
thinkingLevel: source === "explicit" ? "minimal" : undefined,
|
|
modelRuntime: harness.session.modelRuntime,
|
|
settingsManager: harness.settingsManager,
|
|
sessionManager: SessionManager.inMemory(harness.tempDir),
|
|
resourceLoader: createTestResourceLoader(),
|
|
});
|
|
try {
|
|
const expected = !reasoning
|
|
? "off"
|
|
: source === "explicit"
|
|
? "minimal"
|
|
: source === "per-model"
|
|
? "low"
|
|
: source === "global"
|
|
? "high"
|
|
: "medium";
|
|
expect(session.thinkingLevel).toBe(expected);
|
|
await session.setModel(second);
|
|
expect(session.thinkingLevel).toBe(expected);
|
|
} finally {
|
|
session.dispose();
|
|
}
|
|
});
|
|
|
|
it("does not restore the runtime clamp cache when reopening a session", async () => {
|
|
const harness = await createHarness({
|
|
models: [
|
|
{ id: "faux-1", reasoning: true },
|
|
{ id: "faux-2", reasoning: false },
|
|
],
|
|
settings: { defaultThinkingLevel: "high" },
|
|
});
|
|
harnesses.push(harness);
|
|
harness.session.setThinkingLevel("high");
|
|
await harness.session.setModel(harness.getModel("faux-2")!);
|
|
harness.sessionManager.appendMessage({ role: "user", content: "hello", timestamp: Date.now() });
|
|
|
|
const { session } = await createAgentSession({
|
|
cwd: harness.tempDir,
|
|
modelRuntime: harness.session.modelRuntime,
|
|
settingsManager: harness.settingsManager,
|
|
sessionManager: harness.sessionManager,
|
|
resourceLoader: createTestResourceLoader(),
|
|
});
|
|
try {
|
|
expect(session.thinkingLevel).toBe("off");
|
|
await session.setModel(harness.getModel("faux-1")!);
|
|
expect(session.thinkingLevel).toBe("off");
|
|
} finally {
|
|
session.dispose();
|
|
}
|
|
});
|
|
});
|