mirror of
https://github.com/claude-code-best/claude-code.git
synced 2026-06-17 22:05:50 +00:00
feat: 添加 Bedrock API 客户端及 API 层增强
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,23 +1,23 @@
|
||||
import { describe, expect, test, beforeEach, afterEach, mock } from "bun:test";
|
||||
import { describe, expect, test, beforeEach, afterEach, mock } from 'bun:test'
|
||||
|
||||
// Mock heavy dependencies to avoid import chain issues
|
||||
mock.module("src/utils/thinking.js", () => ({
|
||||
mock.module('src/utils/thinking.js', () => ({
|
||||
isUltrathinkEnabled: () => false,
|
||||
}));
|
||||
mock.module("src/utils/settings/settings.js", () => ({
|
||||
}))
|
||||
mock.module('src/utils/settings/settings.js', () => ({
|
||||
getInitialSettings: () => ({}),
|
||||
}));
|
||||
mock.module("src/utils/auth.js", () => ({
|
||||
}))
|
||||
mock.module('src/utils/auth.js', () => ({
|
||||
isProSubscriber: () => false,
|
||||
isMaxSubscriber: () => false,
|
||||
isTeamSubscriber: () => false,
|
||||
}));
|
||||
mock.module("src/services/analytics/growthbook.js", () => ({
|
||||
getFeatureValue_CACHED_MAY_BE_STALE: () => null,
|
||||
}));
|
||||
mock.module("src/utils/model/modelSupportOverrides.js", () => ({
|
||||
}))
|
||||
mock.module('src/services/analytics/growthbook.js', () => ({
|
||||
getFeatureValue_CACHED_MAY_BE_STALE: (_key: string, defaultValue: unknown) => defaultValue ?? {},
|
||||
}))
|
||||
mock.module('src/utils/model/modelSupportOverrides.js', () => ({
|
||||
get3PModelCapabilityOverride: () => undefined,
|
||||
}));
|
||||
}))
|
||||
|
||||
const {
|
||||
isEffortLevel,
|
||||
@@ -27,229 +27,249 @@ const {
|
||||
getEffortLevelDescription,
|
||||
resolvePickerEffortPersistence,
|
||||
EFFORT_LEVELS,
|
||||
} = await import("src/utils/effort.js");
|
||||
} = await import('src/utils/effort.js')
|
||||
|
||||
// ─── EFFORT_LEVELS constant ────────────────────────────────────────────
|
||||
|
||||
describe("EFFORT_LEVELS", () => {
|
||||
test("contains the four canonical levels", () => {
|
||||
expect(EFFORT_LEVELS).toEqual(["low", "medium", "high", "max"]);
|
||||
});
|
||||
});
|
||||
describe('EFFORT_LEVELS', () => {
|
||||
test('contains the five canonical levels', () => {
|
||||
expect(EFFORT_LEVELS).toEqual(['low', 'medium', 'high', 'xhigh', 'max'])
|
||||
})
|
||||
})
|
||||
|
||||
// ─── isEffortLevel ─────────────────────────────────────────────────────
|
||||
|
||||
describe("isEffortLevel", () => {
|
||||
describe('isEffortLevel', () => {
|
||||
test("returns true for 'low'", () => {
|
||||
expect(isEffortLevel("low")).toBe(true);
|
||||
});
|
||||
expect(isEffortLevel('low')).toBe(true)
|
||||
})
|
||||
|
||||
test("returns true for 'medium'", () => {
|
||||
expect(isEffortLevel("medium")).toBe(true);
|
||||
});
|
||||
expect(isEffortLevel('medium')).toBe(true)
|
||||
})
|
||||
|
||||
test("returns true for 'high'", () => {
|
||||
expect(isEffortLevel("high")).toBe(true);
|
||||
});
|
||||
expect(isEffortLevel('high')).toBe(true)
|
||||
})
|
||||
|
||||
test("returns true for 'max'", () => {
|
||||
expect(isEffortLevel("max")).toBe(true);
|
||||
});
|
||||
expect(isEffortLevel('max')).toBe(true)
|
||||
})
|
||||
|
||||
test("returns false for 'invalid'", () => {
|
||||
expect(isEffortLevel("invalid")).toBe(false);
|
||||
});
|
||||
expect(isEffortLevel('invalid')).toBe(false)
|
||||
})
|
||||
|
||||
test("returns false for empty string", () => {
|
||||
expect(isEffortLevel("")).toBe(false);
|
||||
});
|
||||
});
|
||||
test('returns false for empty string', () => {
|
||||
expect(isEffortLevel('')).toBe(false)
|
||||
})
|
||||
})
|
||||
|
||||
// ─── parseEffortValue ──────────────────────────────────────────────────
|
||||
|
||||
describe("parseEffortValue", () => {
|
||||
test("returns undefined for undefined", () => {
|
||||
expect(parseEffortValue(undefined)).toBeUndefined();
|
||||
});
|
||||
describe('parseEffortValue', () => {
|
||||
test('returns undefined for undefined', () => {
|
||||
expect(parseEffortValue(undefined)).toBeUndefined()
|
||||
})
|
||||
|
||||
test("returns undefined for null", () => {
|
||||
expect(parseEffortValue(null)).toBeUndefined();
|
||||
});
|
||||
test('returns undefined for null', () => {
|
||||
expect(parseEffortValue(null)).toBeUndefined()
|
||||
})
|
||||
|
||||
test("returns undefined for empty string", () => {
|
||||
expect(parseEffortValue("")).toBeUndefined();
|
||||
});
|
||||
test('returns undefined for empty string', () => {
|
||||
expect(parseEffortValue('')).toBeUndefined()
|
||||
})
|
||||
|
||||
test("returns number for integer input", () => {
|
||||
expect(parseEffortValue(42)).toBe(42);
|
||||
});
|
||||
test('returns number for integer input', () => {
|
||||
expect(parseEffortValue(42)).toBe(42)
|
||||
})
|
||||
|
||||
test("returns string for valid effort level string", () => {
|
||||
expect(parseEffortValue("low")).toBe("low");
|
||||
expect(parseEffortValue("medium")).toBe("medium");
|
||||
expect(parseEffortValue("high")).toBe("high");
|
||||
expect(parseEffortValue("max")).toBe("max");
|
||||
});
|
||||
test('returns string for valid effort level string', () => {
|
||||
expect(parseEffortValue('low')).toBe('low')
|
||||
expect(parseEffortValue('medium')).toBe('medium')
|
||||
expect(parseEffortValue('high')).toBe('high')
|
||||
expect(parseEffortValue('max')).toBe('max')
|
||||
})
|
||||
|
||||
test("parses numeric string to number", () => {
|
||||
expect(parseEffortValue("42")).toBe(42);
|
||||
});
|
||||
test('parses numeric string to number', () => {
|
||||
expect(parseEffortValue('42')).toBe(42)
|
||||
})
|
||||
|
||||
test("returns undefined for invalid string", () => {
|
||||
expect(parseEffortValue("invalid")).toBeUndefined();
|
||||
});
|
||||
test('returns undefined for invalid string', () => {
|
||||
expect(parseEffortValue('invalid')).toBeUndefined()
|
||||
})
|
||||
|
||||
test("non-integer number falls through to string parsing (parseInt truncates)", () => {
|
||||
test('non-integer number falls through to string parsing (parseInt truncates)', () => {
|
||||
// 3.14 fails isValidNumericEffort, then String(3.14) -> "3.14" -> parseInt = 3
|
||||
expect(parseEffortValue(3.14)).toBe(3);
|
||||
});
|
||||
expect(parseEffortValue(3.14)).toBe(3)
|
||||
})
|
||||
|
||||
test("handles case-insensitive effort level strings", () => {
|
||||
expect(parseEffortValue("LOW")).toBe("low");
|
||||
expect(parseEffortValue("HIGH")).toBe("high");
|
||||
});
|
||||
});
|
||||
test('handles case-insensitive effort level strings', () => {
|
||||
expect(parseEffortValue('LOW')).toBe('low')
|
||||
expect(parseEffortValue('HIGH')).toBe('high')
|
||||
})
|
||||
})
|
||||
|
||||
// ─── isValidNumericEffort ──────────────────────────────────────────────
|
||||
|
||||
describe("isValidNumericEffort", () => {
|
||||
test("returns true for integer", () => {
|
||||
expect(isValidNumericEffort(50)).toBe(true);
|
||||
});
|
||||
describe('isValidNumericEffort', () => {
|
||||
test('returns true for integer', () => {
|
||||
expect(isValidNumericEffort(50)).toBe(true)
|
||||
})
|
||||
|
||||
test("returns true for zero", () => {
|
||||
expect(isValidNumericEffort(0)).toBe(true);
|
||||
});
|
||||
test('returns true for zero', () => {
|
||||
expect(isValidNumericEffort(0)).toBe(true)
|
||||
})
|
||||
|
||||
test("returns true for negative integer", () => {
|
||||
expect(isValidNumericEffort(-1)).toBe(true);
|
||||
});
|
||||
test('returns true for negative integer', () => {
|
||||
expect(isValidNumericEffort(-1)).toBe(true)
|
||||
})
|
||||
|
||||
test("returns false for float", () => {
|
||||
expect(isValidNumericEffort(3.14)).toBe(false);
|
||||
});
|
||||
test('returns false for float', () => {
|
||||
expect(isValidNumericEffort(3.14)).toBe(false)
|
||||
})
|
||||
|
||||
test("returns false for NaN", () => {
|
||||
expect(isValidNumericEffort(NaN)).toBe(false);
|
||||
});
|
||||
test('returns false for NaN', () => {
|
||||
expect(isValidNumericEffort(NaN)).toBe(false)
|
||||
})
|
||||
|
||||
test("returns false for Infinity", () => {
|
||||
expect(isValidNumericEffort(Infinity)).toBe(false);
|
||||
});
|
||||
});
|
||||
test('returns false for Infinity', () => {
|
||||
expect(isValidNumericEffort(Infinity)).toBe(false)
|
||||
})
|
||||
})
|
||||
|
||||
// ─── convertEffortValueToLevel ─────────────────────────────────────────
|
||||
|
||||
describe("convertEffortValueToLevel", () => {
|
||||
test("returns valid effort level string as-is", () => {
|
||||
expect(convertEffortValueToLevel("low")).toBe("low");
|
||||
expect(convertEffortValueToLevel("medium")).toBe("medium");
|
||||
expect(convertEffortValueToLevel("high")).toBe("high");
|
||||
expect(convertEffortValueToLevel("max")).toBe("max");
|
||||
});
|
||||
describe('convertEffortValueToLevel', () => {
|
||||
test('returns valid effort level string as-is', () => {
|
||||
expect(convertEffortValueToLevel('low')).toBe('low')
|
||||
expect(convertEffortValueToLevel('medium')).toBe('medium')
|
||||
expect(convertEffortValueToLevel('high')).toBe('high')
|
||||
expect(convertEffortValueToLevel('max')).toBe('max')
|
||||
})
|
||||
|
||||
test("returns 'high' for unknown string", () => {
|
||||
expect(convertEffortValueToLevel("unknown" as any)).toBe("high");
|
||||
});
|
||||
expect(convertEffortValueToLevel('unknown' as any)).toBe('high')
|
||||
})
|
||||
|
||||
test("non-ant numeric value returns 'high'", () => {
|
||||
const saved = process.env.USER_TYPE;
|
||||
delete process.env.USER_TYPE;
|
||||
const saved = process.env.USER_TYPE
|
||||
delete process.env.USER_TYPE
|
||||
|
||||
expect(convertEffortValueToLevel(50)).toBe("high");
|
||||
expect(convertEffortValueToLevel(100)).toBe("high");
|
||||
expect(convertEffortValueToLevel(50)).toBe('high')
|
||||
expect(convertEffortValueToLevel(100)).toBe('high')
|
||||
|
||||
process.env.USER_TYPE = saved;
|
||||
});
|
||||
process.env.USER_TYPE = saved
|
||||
})
|
||||
|
||||
describe("ant numeric mapping", () => {
|
||||
let savedUserType: string | undefined;
|
||||
describe('ant numeric mapping', () => {
|
||||
let savedUserType: string | undefined
|
||||
|
||||
beforeEach(() => {
|
||||
savedUserType = process.env.USER_TYPE;
|
||||
process.env.USER_TYPE = "ant";
|
||||
});
|
||||
savedUserType = process.env.USER_TYPE
|
||||
process.env.USER_TYPE = 'ant'
|
||||
})
|
||||
|
||||
afterEach(() => {
|
||||
if (savedUserType === undefined) {
|
||||
delete process.env.USER_TYPE;
|
||||
delete process.env.USER_TYPE
|
||||
} else {
|
||||
process.env.USER_TYPE = savedUserType;
|
||||
process.env.USER_TYPE = savedUserType
|
||||
}
|
||||
});
|
||||
})
|
||||
|
||||
test("value <= 50 maps to 'low'", () => {
|
||||
expect(convertEffortValueToLevel(50)).toBe("low");
|
||||
expect(convertEffortValueToLevel(0)).toBe("low");
|
||||
expect(convertEffortValueToLevel(-10)).toBe("low");
|
||||
});
|
||||
expect(convertEffortValueToLevel(50)).toBe('low')
|
||||
expect(convertEffortValueToLevel(0)).toBe('low')
|
||||
expect(convertEffortValueToLevel(-10)).toBe('low')
|
||||
})
|
||||
|
||||
test("value 51-85 maps to 'medium'", () => {
|
||||
expect(convertEffortValueToLevel(51)).toBe("medium");
|
||||
expect(convertEffortValueToLevel(85)).toBe("medium");
|
||||
});
|
||||
expect(convertEffortValueToLevel(51)).toBe('medium')
|
||||
expect(convertEffortValueToLevel(85)).toBe('medium')
|
||||
})
|
||||
|
||||
test("value 86-100 maps to 'high'", () => {
|
||||
expect(convertEffortValueToLevel(86)).toBe("high");
|
||||
expect(convertEffortValueToLevel(100)).toBe("high");
|
||||
});
|
||||
expect(convertEffortValueToLevel(86)).toBe('high')
|
||||
expect(convertEffortValueToLevel(100)).toBe('high')
|
||||
})
|
||||
|
||||
test("value > 100 maps to 'max'", () => {
|
||||
expect(convertEffortValueToLevel(101)).toBe("max");
|
||||
expect(convertEffortValueToLevel(200)).toBe("max");
|
||||
});
|
||||
});
|
||||
});
|
||||
expect(convertEffortValueToLevel(101)).toBe('max')
|
||||
expect(convertEffortValueToLevel(200)).toBe('max')
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
// ─── getEffortLevelDescription ─────────────────────────────────────────
|
||||
|
||||
describe("getEffortLevelDescription", () => {
|
||||
describe('getEffortLevelDescription', () => {
|
||||
test("returns description for 'low'", () => {
|
||||
const desc = getEffortLevelDescription("low");
|
||||
expect(desc).toContain("Quick");
|
||||
});
|
||||
const desc = getEffortLevelDescription('low')
|
||||
expect(desc).toContain('Quick')
|
||||
})
|
||||
|
||||
test("returns description for 'medium'", () => {
|
||||
const desc = getEffortLevelDescription("medium");
|
||||
expect(desc).toContain("Balanced");
|
||||
});
|
||||
const desc = getEffortLevelDescription('medium')
|
||||
expect(desc).toContain('Balanced')
|
||||
})
|
||||
|
||||
test("returns description for 'high'", () => {
|
||||
const desc = getEffortLevelDescription("high");
|
||||
expect(desc).toContain("Comprehensive");
|
||||
});
|
||||
const desc = getEffortLevelDescription('high')
|
||||
expect(desc).toContain('Comprehensive')
|
||||
})
|
||||
|
||||
test("returns description for 'max'", () => {
|
||||
const desc = getEffortLevelDescription("max");
|
||||
expect(desc).toContain("Maximum");
|
||||
});
|
||||
});
|
||||
const desc = getEffortLevelDescription('max')
|
||||
expect(desc).toContain('Maximum')
|
||||
})
|
||||
})
|
||||
|
||||
// ─── resolvePickerEffortPersistence ────────────────────────────────────
|
||||
|
||||
describe("resolvePickerEffortPersistence", () => {
|
||||
test("returns undefined when picked matches model default and no prior persistence", () => {
|
||||
const result = resolvePickerEffortPersistence("high", "high", undefined, false);
|
||||
expect(result).toBeUndefined();
|
||||
});
|
||||
describe('resolvePickerEffortPersistence', () => {
|
||||
test('returns undefined when picked matches model default and no prior persistence', () => {
|
||||
const result = resolvePickerEffortPersistence(
|
||||
'high',
|
||||
'high',
|
||||
undefined,
|
||||
false,
|
||||
)
|
||||
expect(result).toBeUndefined()
|
||||
})
|
||||
|
||||
test("returns picked when it differs from model default", () => {
|
||||
const result = resolvePickerEffortPersistence("low", "high", undefined, false);
|
||||
expect(result).toBe("low");
|
||||
});
|
||||
test('returns picked when it differs from model default', () => {
|
||||
const result = resolvePickerEffortPersistence(
|
||||
'low',
|
||||
'high',
|
||||
undefined,
|
||||
false,
|
||||
)
|
||||
expect(result).toBe('low')
|
||||
})
|
||||
|
||||
test("returns picked when priorPersisted is set (even if same as default)", () => {
|
||||
const result = resolvePickerEffortPersistence("high", "high", "high", false);
|
||||
expect(result).toBe("high");
|
||||
});
|
||||
test('returns picked when priorPersisted is set (even if same as default)', () => {
|
||||
const result = resolvePickerEffortPersistence('high', 'high', 'high', false)
|
||||
expect(result).toBe('high')
|
||||
})
|
||||
|
||||
test("returns picked when toggledInPicker is true (even if same as default)", () => {
|
||||
const result = resolvePickerEffortPersistence("high", "high", undefined, true);
|
||||
expect(result).toBe("high");
|
||||
});
|
||||
test('returns picked when toggledInPicker is true (even if same as default)', () => {
|
||||
const result = resolvePickerEffortPersistence(
|
||||
'high',
|
||||
'high',
|
||||
undefined,
|
||||
true,
|
||||
)
|
||||
expect(result).toBe('high')
|
||||
})
|
||||
|
||||
test("returns undefined picked value when no explicit and matches default", () => {
|
||||
const result = resolvePickerEffortPersistence(undefined, "high" as any, undefined, false);
|
||||
expect(result).toBeUndefined();
|
||||
});
|
||||
});
|
||||
test('returns undefined picked value when no explicit and matches default', () => {
|
||||
const result = resolvePickerEffortPersistence(
|
||||
undefined,
|
||||
'high' as any,
|
||||
undefined,
|
||||
false,
|
||||
)
|
||||
expect(result).toBeUndefined()
|
||||
})
|
||||
})
|
||||
|
||||
@@ -360,9 +360,7 @@ export function splitSysPromptPrefix(
|
||||
}
|
||||
|
||||
if (useGlobalCacheFeature) {
|
||||
const boundaryIndex = systemPrompt.findIndex(
|
||||
s => s === SYSTEM_PROMPT_DYNAMIC_BOUNDARY,
|
||||
)
|
||||
const boundaryIndex = systemPrompt.indexOf(SYSTEM_PROMPT_DYNAMIC_BOUNDARY)
|
||||
if (boundaryIndex !== -1) {
|
||||
let attributionHeader: string | undefined
|
||||
let systemPromptPrefix: string | undefined
|
||||
|
||||
@@ -68,7 +68,6 @@ export function filterAllowedSdkBetas(
|
||||
}
|
||||
|
||||
if (isClaudeAISubscriber()) {
|
||||
// biome-ignore lint/suspicious/noConsole: intentional warning
|
||||
console.warn(
|
||||
'Warning: Custom betas are only available for API key users. Ignoring provided betas.',
|
||||
)
|
||||
@@ -77,7 +76,6 @@ export function filterAllowedSdkBetas(
|
||||
|
||||
const { allowed, disallowed } = partitionBetasByAllowlist(sdkBetas)
|
||||
for (const beta of disallowed) {
|
||||
// biome-ignore lint/suspicious/noConsole: intentional warning
|
||||
console.warn(
|
||||
`Warning: Beta header '${beta}' is not allowed. Only the following betas are supported: ${ALLOWED_SDK_BETAS.join(', ')}`,
|
||||
)
|
||||
@@ -151,6 +149,7 @@ export function modelSupportsStructuredOutputs(model: string): boolean {
|
||||
canonical.includes('claude-opus-4-1') ||
|
||||
canonical.includes('claude-opus-4-5') ||
|
||||
canonical.includes('claude-opus-4-6') ||
|
||||
canonical.includes('claude-opus-4-7') ||
|
||||
canonical.includes('claude-haiku-4-5')
|
||||
)
|
||||
}
|
||||
@@ -188,7 +187,7 @@ export function modelSupportsAutoMode(model: string): boolean {
|
||||
return true
|
||||
}
|
||||
// External allowlist (firstParty already checked above).
|
||||
return /^claude-(opus|sonnet)-4-6/.test(m)
|
||||
return /^claude-(opus|sonnet)-4-[67]/.test(m)
|
||||
}
|
||||
return false
|
||||
}
|
||||
@@ -275,16 +274,18 @@ export const getAllModelBetas = memoize((model: string): string[] => {
|
||||
betaHeaders.push(REDACT_THINKING_BETA_HEADER)
|
||||
}
|
||||
|
||||
// Add context management beta for tool clearing (ant opt-in) or thinking preservation
|
||||
const antOptedIntoToolClearing =
|
||||
isEnvTruthy(process.env.USE_API_CONTEXT_MANAGEMENT) &&
|
||||
process.env.USER_TYPE === 'ant'
|
||||
// Add context management beta for tool clearing or thinking preservation.
|
||||
// Tool clearing is enabled by default for all users (upstream gates on ant);
|
||||
// thinking preservation activates when the model supports context management.
|
||||
const toolClearingOptIn =
|
||||
isEnvTruthy(process.env.USE_API_CONTEXT_MANAGEMENT) ||
|
||||
modelSupportsContextManagement(model)
|
||||
|
||||
const thinkingPreservationEnabled = modelSupportsContextManagement(model)
|
||||
|
||||
if (
|
||||
shouldIncludeFirstPartyOnlyBetas() &&
|
||||
(antOptedIntoToolClearing || thinkingPreservationEnabled)
|
||||
(toolClearingOptIn || thinkingPreservationEnabled)
|
||||
) {
|
||||
betaHeaders.push(CONTEXT_MANAGEMENT_BETA_HEADER)
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@ export const EFFORT_LEVELS = [
|
||||
'low',
|
||||
'medium',
|
||||
'high',
|
||||
'xhigh',
|
||||
'max',
|
||||
] as const satisfies readonly EffortLevel[]
|
||||
|
||||
@@ -32,7 +33,11 @@ export function modelSupportsEffort(model: string): boolean {
|
||||
return supported3P
|
||||
}
|
||||
// Supported by a subset of Claude 4 models
|
||||
if (m.includes('opus-4-6') || m.includes('sonnet-4-6')) {
|
||||
if (
|
||||
m.includes('opus-4-7') ||
|
||||
m.includes('opus-4-6') ||
|
||||
m.includes('sonnet-4-6')
|
||||
) {
|
||||
return true
|
||||
}
|
||||
// Exclude any other known legacy models (haiku, older opus/sonnet variants)
|
||||
@@ -51,13 +56,32 @@ export function modelSupportsEffort(model: string): boolean {
|
||||
}
|
||||
|
||||
// @[MODEL LAUNCH]: Add the new model to the allowlist if it supports 'max' effort.
|
||||
// Per API docs, 'max' is Opus 4.6 only for public models — other models return an error.
|
||||
// Per API docs, 'max' is Opus 4.6/4.7 only for public models — other models return an error.
|
||||
export function modelSupportsMaxEffort(model: string): boolean {
|
||||
const supported3P = get3PModelCapabilityOverride(model, 'max_effort')
|
||||
if (supported3P !== undefined) {
|
||||
return supported3P
|
||||
}
|
||||
if (model.toLowerCase().includes('opus-4-6')) {
|
||||
if (
|
||||
model.toLowerCase().includes('opus-4-7') ||
|
||||
model.toLowerCase().includes('opus-4-6')
|
||||
) {
|
||||
return true
|
||||
}
|
||||
if (process.env.USER_TYPE === 'ant' && resolveAntModel(model)) {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// @[MODEL LAUNCH]: Add the new model to the allowlist if it supports 'xhigh' effort.
|
||||
// 'xhigh' was introduced with Opus 4.7 as a level between 'high' and 'max'.
|
||||
export function modelSupportsXhighEffort(model: string): boolean {
|
||||
const supported3P = get3PModelCapabilityOverride(model, 'xhigh_effort')
|
||||
if (supported3P !== undefined) {
|
||||
return supported3P
|
||||
}
|
||||
if (model.toLowerCase().includes('opus-4-7')) {
|
||||
return true
|
||||
}
|
||||
if (process.env.USER_TYPE === 'ant' && resolveAntModel(model)) {
|
||||
@@ -97,7 +121,12 @@ export function parseEffortValue(value: unknown): EffortValue | undefined {
|
||||
export function toPersistableEffort(
|
||||
value: EffortValue | undefined,
|
||||
): EffortLevel | undefined {
|
||||
if (value === 'low' || value === 'medium' || value === 'high') {
|
||||
if (
|
||||
value === 'low' ||
|
||||
value === 'medium' ||
|
||||
value === 'high' ||
|
||||
value === 'xhigh'
|
||||
) {
|
||||
return value
|
||||
}
|
||||
if (value === 'max' && process.env.USER_TYPE === 'ant') {
|
||||
@@ -161,6 +190,10 @@ export function resolveAppliedEffort(
|
||||
}
|
||||
const resolved =
|
||||
envOverride ?? appStateEffortValue ?? getDefaultEffortForModel(model)
|
||||
// API rejects 'xhigh' on pre-Opus-4.7 models — downgrade to 'high'.
|
||||
if (resolved === 'xhigh' && !modelSupportsXhighEffort(model)) {
|
||||
return 'high'
|
||||
}
|
||||
// API rejects 'max' on non-Opus-4.6 models — downgrade to 'high'.
|
||||
if (resolved === 'max' && !modelSupportsMaxEffort(model)) {
|
||||
return 'high'
|
||||
@@ -231,8 +264,10 @@ export function getEffortLevelDescription(level: EffortLevel): string {
|
||||
return 'Balanced approach with standard implementation and testing'
|
||||
case 'high':
|
||||
return 'Comprehensive implementation with extensive testing and documentation'
|
||||
case 'xhigh':
|
||||
return 'Extended reasoning beyond high, short of max (Opus 4.7 only)'
|
||||
case 'max':
|
||||
return 'Maximum capability with deepest reasoning (Opus 4.6 only)'
|
||||
return 'Maximum capability with deepest reasoning (Opus 4.6/4.7 only)'
|
||||
}
|
||||
}
|
||||
|
||||
@@ -308,7 +343,10 @@ export function getDefaultEffortForModel(
|
||||
|
||||
// Default effort on Opus 4.6 to medium for Pro.
|
||||
// Max/Team also get medium when the tengu_grey_step2 config is enabled.
|
||||
if (model.toLowerCase().includes('opus-4-6')) {
|
||||
if (
|
||||
model.toLowerCase().includes('opus-4-7') ||
|
||||
model.toLowerCase().includes('opus-4-6')
|
||||
) {
|
||||
if (isProSubscriber()) {
|
||||
return 'medium'
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user