231 lines
7.5 KiB
TypeScript

import { describe, expect, it } from "bun:test"
import { createAnthropicEffortHook } from "./index"
interface ChatParamsInput {
sessionID: string
agent: { name?: string }
model: { providerID: string; modelID: string; id?: string; api?: { npm?: string } }
provider: { id: string }
message: { variant?: string }
}
interface ChatParamsOutput {
temperature?: number
topP?: number
topK?: number
options: Record<string, unknown>
}
function createMockParams(overrides: {
providerID?: string
modelID?: string
variant?: string
agentName?: string
existingOptions?: Record<string, unknown>
}): { input: ChatParamsInput; output: ChatParamsOutput } {
const providerID = overrides.providerID ?? "anthropic"
const modelID = overrides.modelID ?? "claude-opus-4-6"
const variant = "variant" in overrides ? overrides.variant : "max"
const agentName = overrides.agentName ?? "sisyphus"
const existingOptions = overrides.existingOptions ?? {}
return {
input: {
sessionID: "test-session",
agent: { name: agentName },
model: { providerID, modelID },
provider: { id: providerID },
message: { variant },
},
output: {
temperature: 0.1,
options: { ...existingOptions },
},
}
}
describe("createAnthropicEffortHook", () => {
describe("opus 4-6 with variant max", () => {
it("should inject effort max for anthropic opus-4-6 with variant max", async () => {
//#given anthropic opus-4-6 model with variant max
const hook = createAnthropicEffortHook()
const { input, output } = createMockParams({})
//#when chat.params hook is called
await hook["chat.params"](input, output)
//#then effort should be injected into options
expect(output.options.effort).toBe("max")
})
it("should inject effort max for github-copilot claude-opus-4-6", async () => {
//#given github-copilot provider with claude-opus-4-6
const hook = createAnthropicEffortHook()
const { input, output } = createMockParams({
providerID: "github-copilot",
modelID: "claude-opus-4-6",
})
//#when chat.params hook is called
await hook["chat.params"](input, output)
//#then effort should be injected (github-copilot resolves to anthropic)
expect(output.options.effort).toBe("max")
})
it("should inject effort max for opencode provider with claude-opus-4-6", async () => {
//#given opencode provider with claude-opus-4-6
const hook = createAnthropicEffortHook()
const { input, output } = createMockParams({
providerID: "opencode",
modelID: "claude-opus-4-6",
})
//#when chat.params hook is called
await hook["chat.params"](input, output)
//#then effort should be injected
expect(output.options.effort).toBe("max")
})
it("should inject effort max for google-vertex-anthropic provider", async () => {
//#given google-vertex-anthropic provider with claude-opus-4-6
const hook = createAnthropicEffortHook()
const { input, output } = createMockParams({
providerID: "google-vertex-anthropic",
modelID: "claude-opus-4-6",
})
//#when chat.params hook is called
await hook["chat.params"](input, output)
//#then effort should be injected
expect(output.options.effort).toBe("max")
})
it("should handle normalized model ID with dots (opus-4.6)", async () => {
//#given model ID with dots instead of hyphens
const hook = createAnthropicEffortHook()
const { input, output } = createMockParams({
modelID: "claude-opus-4.6",
})
//#when chat.params hook is called
await hook["chat.params"](input, output)
//#then should normalize and inject effort
expect(output.options.effort).toBe("max")
})
})
describe("conditions NOT met - should skip", () => {
it("should NOT inject effort when variant is not max", async () => {
//#given opus-4-6 with variant high (not max)
const hook = createAnthropicEffortHook()
const { input, output } = createMockParams({ variant: "high" })
//#when chat.params hook is called
await hook["chat.params"](input, output)
//#then effort should NOT be injected
expect(output.options.effort).toBeUndefined()
})
it("should NOT inject effort when variant is undefined", async () => {
//#given opus-4-6 with no variant
const hook = createAnthropicEffortHook()
const { input, output } = createMockParams({ variant: undefined })
//#when chat.params hook is called
await hook["chat.params"](input, output)
//#then effort should NOT be injected
expect(output.options.effort).toBeUndefined()
})
it("should NOT inject effort for non-opus model", async () => {
//#given claude-sonnet-4-6 (not opus)
const hook = createAnthropicEffortHook()
const { input, output } = createMockParams({
modelID: "claude-sonnet-4-6",
})
//#when chat.params hook is called
await hook["chat.params"](input, output)
//#then effort should NOT be injected
expect(output.options.effort).toBeUndefined()
})
it("should NOT inject effort for non-anthropic provider with non-claude model", async () => {
//#given openai provider with gpt model
const hook = createAnthropicEffortHook()
const { input, output } = createMockParams({
providerID: "openai",
modelID: "gpt-5.2",
})
//#when chat.params hook is called
await hook["chat.params"](input, output)
//#then effort should NOT be injected
expect(output.options.effort).toBeUndefined()
})
it("should NOT throw when model.modelID is undefined", async () => {
//#given model with undefined modelID (runtime edge case)
const hook = createAnthropicEffortHook()
const input = {
sessionID: "test-session",
agent: { name: "sisyphus" },
model: { providerID: "anthropic", modelID: undefined as unknown as string },
provider: { id: "anthropic" },
message: { variant: "max" as const },
}
const output = { temperature: 0.1, options: {} }
//#when chat.params hook is called with undefined modelID
await hook["chat.params"](input, output)
//#then should gracefully skip without throwing
expect(output.options.effort).toBeUndefined()
})
})
describe("preserves existing options", () => {
it("should NOT overwrite existing effort if already set", async () => {
//#given options already have effort set
const hook = createAnthropicEffortHook()
const { input, output } = createMockParams({
existingOptions: { effort: "high" },
})
//#when chat.params hook is called
await hook["chat.params"](input, output)
//#then existing effort should be preserved
expect(output.options.effort).toBe("high")
})
it("should preserve other existing options when injecting effort", async () => {
//#given options with existing thinking config
const hook = createAnthropicEffortHook()
const { input, output } = createMockParams({
existingOptions: {
thinking: { type: "enabled", budgetTokens: 31999 },
},
})
//#when chat.params hook is called
await hook["chat.params"](input, output)
//#then effort should be added without affecting thinking
expect(output.options.effort).toBe("max")
expect(output.options.thinking).toEqual({
type: "enabled",
budgetTokens: 31999,
})
})
})
})