231 lines
7.5 KiB
TypeScript
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,
|
|
})
|
|
})
|
|
})
|
|
})
|