mirror of
https://github.com/wu736139669/hapi.git
synced 2026-08-05 06:24:37 +00:00
fix claude model option merging (#726)
This commit is contained in:
@@ -16,6 +16,35 @@ describe('getModelOptionsForFlavor', () => {
|
||||
expect(options.some((o) => o.value === 'opus')).toBe(true)
|
||||
})
|
||||
|
||||
it('keeps Claude presets when explicit options only include Sonnet models', () => {
|
||||
const options = getModelOptionsForFlavor('claude', null, [
|
||||
{ value: null, label: 'Default' },
|
||||
{ value: 'sonnet', label: 'Sonnet' },
|
||||
{ value: 'sonnet[1m]', label: 'Sonnet 1M' }
|
||||
])
|
||||
expect(options).toEqual([
|
||||
{ value: null, label: 'Default' },
|
||||
{ value: 'sonnet', label: 'Sonnet' },
|
||||
{ value: 'sonnet[1m]', label: 'Sonnet 1M' },
|
||||
{ value: 'opus', label: 'Opus' },
|
||||
{ value: 'opus[1m]', label: 'Opus 1M' }
|
||||
])
|
||||
})
|
||||
|
||||
it('adds non-preset Claude options without hiding Opus presets', () => {
|
||||
const options = getModelOptionsForFlavor('claude', null, [
|
||||
{ value: 'claude-opus-4-1-20250805', label: 'Claude Opus 4.1' }
|
||||
])
|
||||
expect(options).toEqual([
|
||||
{ value: null, label: 'Default' },
|
||||
{ value: 'claude-opus-4-1-20250805', label: 'Claude Opus 4.1' },
|
||||
{ value: 'sonnet', label: 'Sonnet' },
|
||||
{ value: 'sonnet[1m]', label: 'Sonnet 1M' },
|
||||
{ value: 'opus', label: 'Opus' },
|
||||
{ value: 'opus[1m]', label: 'Opus 1M' }
|
||||
])
|
||||
})
|
||||
|
||||
it('includes custom Gemini model from env/config in options', () => {
|
||||
const options = getModelOptionsForFlavor('gemini', 'gemini-custom-experiment')
|
||||
expect(options.some((o) => o.value === 'gemini-custom-experiment')).toBe(true)
|
||||
@@ -94,6 +123,14 @@ describe('getNextModelForFlavor', () => {
|
||||
expect(next).not.toBeNull()
|
||||
})
|
||||
|
||||
it('cycles through Claude presets when explicit options only include Sonnet models', () => {
|
||||
const next = getNextModelForFlavor('claude', 'sonnet[1m]', [
|
||||
{ value: 'sonnet', label: 'Sonnet' },
|
||||
{ value: 'sonnet[1m]', label: 'Sonnet 1M' }
|
||||
])
|
||||
expect(next).toBe('opus')
|
||||
})
|
||||
|
||||
it('cycles explicit model options', () => {
|
||||
const next = getNextModelForFlavor('codex', 'gpt-5.5', [
|
||||
{ value: 'gpt-5.5', label: 'GPT-5.5' },
|
||||
|
||||
@@ -28,6 +28,39 @@ function withCurrentModelOption(options: ModelOption[], currentModel?: string |
|
||||
return nextOptions
|
||||
}
|
||||
|
||||
function getClaudeModelOptions(currentModel?: string | null, customOptions?: ModelOption[]): ModelOption[] {
|
||||
if (!customOptions || customOptions.length === 0) {
|
||||
return getClaudeComposerModelOptions(currentModel)
|
||||
}
|
||||
|
||||
const options = getClaudeComposerModelOptions(currentModel)
|
||||
const nextOptions = [...options]
|
||||
let insertIndex = Math.max(1, nextOptions.findIndex((option) => option.value !== null))
|
||||
|
||||
for (const option of customOptions) {
|
||||
const normalizedValue = normalizeCurrentModel(option.value)
|
||||
if (!normalizedValue) {
|
||||
continue
|
||||
}
|
||||
|
||||
const existingIndex = nextOptions.findIndex((nextOption) => nextOption.value === normalizedValue)
|
||||
if (existingIndex >= 0) {
|
||||
if (nextOptions[existingIndex]?.label === normalizedValue) {
|
||||
nextOptions[existingIndex] = option
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
nextOptions.splice(insertIndex, 0, {
|
||||
value: normalizedValue,
|
||||
label: option.label
|
||||
})
|
||||
insertIndex += 1
|
||||
}
|
||||
|
||||
return nextOptions
|
||||
}
|
||||
|
||||
function getGeminiModelOptions(currentModel?: string | null): ModelOption[] {
|
||||
const options = MODEL_OPTIONS.gemini.map((m) => ({
|
||||
value: m.value === 'auto' ? null : m.value,
|
||||
@@ -50,6 +83,9 @@ export function getModelOptionsForFlavor(
|
||||
currentModel?: string | null,
|
||||
customOptions?: ModelOption[]
|
||||
): ModelOption[] {
|
||||
if (flavor === 'claude') {
|
||||
return getClaudeModelOptions(currentModel, customOptions)
|
||||
}
|
||||
if (customOptions && customOptions.length > 0) {
|
||||
return withCurrentModelOption(customOptions, currentModel)
|
||||
}
|
||||
@@ -69,7 +105,7 @@ export function getModelOptionsForFlavor(
|
||||
if (flavor === 'kimi') {
|
||||
return withCurrentModelOption([{ value: null, label: 'Default' }], currentModel)
|
||||
}
|
||||
return getClaudeComposerModelOptions(currentModel)
|
||||
return getClaudeModelOptions(currentModel)
|
||||
}
|
||||
|
||||
export function getNextModelForFlavor(
|
||||
@@ -77,6 +113,14 @@ export function getNextModelForFlavor(
|
||||
currentModel?: string | null,
|
||||
customOptions?: ModelOption[]
|
||||
): string | null {
|
||||
if (flavor === 'claude') {
|
||||
const options = getClaudeModelOptions(currentModel, customOptions)
|
||||
const currentIndex = options.findIndex((option) => option.value === (normalizeCurrentModel(currentModel) ?? null))
|
||||
if (currentIndex === -1) {
|
||||
return options[0]?.value ?? null
|
||||
}
|
||||
return options[(currentIndex + 1) % options.length]?.value ?? null
|
||||
}
|
||||
if (customOptions && customOptions.length > 0) {
|
||||
const options = getModelOptionsForFlavor(flavor, currentModel, customOptions)
|
||||
const currentIndex = options.findIndex((option) => option.value === (normalizeCurrentModel(currentModel) ?? null))
|
||||
|
||||
Reference in New Issue
Block a user