mirror of
https://github.com/wu736139669/hapi.git
synced 2026-11-07 01:21:37 +00:00
* fix(claude): drop the obsolete 1M model presets The opus, sonnet and fable aliases now resolve to models with a native 1M window (Opus 5.5, Sonnet 5.5, Fable 5.1), so Claude Code documents the "[1m]" suffix as having no effect on them and its own /model picker no longer lists 1M rows. Remove Sonnet 1M / Opus 1M / Fable 1M from the shared, Android and iOS catalogs. Sessions stored with a "[1m]" value keep working: the composer keeps the current model selectable, and the context-budget fallback treats any "[1m]" model and the remaining aliases as 1M, so local-mode sessions on plain opus/sonnet no longer show a 200k budget. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(cli): seed a 1M turn-1 context window for bare Claude aliases With the 1M presets gone, a session on plain fable/opus/sonnet must not seed 200k before its first result: those aliases now resolve to models with a native 1M window. Seed 1M when the selected model is a preset alias; an explicit full id still seeds conservatively until its result arrives. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(claude): offer the four current Claude models as presets The Claude presets now mirror the aliases Claude Code's /model lists, in its order: Opus, Fable, Sonnet, Haiku. Each alias resolves to the newest model of its family, and all four have a native 1M window, so neither 1M variants nor pinned older versions are offered. Haiku was missing. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(claude): read the effective context window from get_context_usage The 1M seed for the aliases is wrong for users who disable 1M (CLAUDE_CODE_DISABLE_1M_CONTEXT) or cap it with autoCompactWindow. In remote mode, ask Claude via the get_context_usage control request after each system/init and use its maxTokens, which is what /context shows. A result's raw contextWindow no longer overwrites that value. Older CLIs that reject the request keep the existing seed. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> Co-authored-by: weishu <twsxtd@gmail.com>
149 lines
5.9 KiB
TypeScript
149 lines
5.9 KiB
TypeScript
import type { ClaudeModelSummary } from './apiTypes'
|
|
|
|
// Fallback aliases in Claude Code's /model order, used until its live catalog
|
|
// arrives. Each resolves to the newest model of its family.
|
|
export const CLAUDE_MODEL_LABELS = {
|
|
opus: 'Opus',
|
|
fable: 'Fable',
|
|
sonnet: 'Sonnet',
|
|
haiku: 'Haiku'
|
|
} as const
|
|
|
|
export type ClaudeModelPreset = keyof typeof CLAUDE_MODEL_LABELS
|
|
export const CLAUDE_MODEL_PRESETS = Object.keys(CLAUDE_MODEL_LABELS) as ClaudeModelPreset[]
|
|
|
|
export const GEMINI_MODEL_LABELS = {
|
|
'gemini-3.1-pro-preview': 'Gemini 3.1 Pro Preview',
|
|
'gemini-3-flash-preview': 'Gemini 3 Flash Preview',
|
|
'gemini-2.5-pro': 'Gemini 2.5 Pro',
|
|
'gemini-2.5-flash': 'Gemini 2.5 Flash',
|
|
'gemini-2.5-flash-lite': 'Gemini 2.5 Flash Lite',
|
|
} as const
|
|
|
|
export type GeminiModelPreset = keyof typeof GEMINI_MODEL_LABELS
|
|
export const GEMINI_MODEL_PRESETS = Object.keys(GEMINI_MODEL_LABELS) as GeminiModelPreset[]
|
|
export const DEFAULT_GEMINI_MODEL: GeminiModelPreset = 'gemini-2.5-pro'
|
|
|
|
// Order and labels mirror `agy models` output (the agy CLI's own listing) so the
|
|
// HAPI picker matches what users see in the terminal. IDs follow agy's
|
|
// `<model>-<effort>` convention (e.g. `gemini-3.5-flash-low` verified accepted by
|
|
// `agy --model`). NOTE: agy fetches the live list server-side and `agy models`
|
|
// needs an interactive keyring unlock, so this stays a hand-maintained mirror —
|
|
// update it when agy's listing changes.
|
|
export const AGY_MODEL_LABELS = {
|
|
'gemini-3.7-flash-high': 'Gemini 3.7 Flash (High)',
|
|
'gemini-3.7-flash-medium': 'Gemini 3.7 Flash (Medium)',
|
|
'gemini-3.7-flash-low': 'Gemini 3.7 Flash (Low)',
|
|
'gemini-3.6-flash-high': 'Gemini 3.6 Flash (High)',
|
|
'gemini-3.6-flash-medium': 'Gemini 3.6 Flash (Medium)',
|
|
'gemini-3.6-flash-low': 'Gemini 3.6 Flash (Low)',
|
|
'gemini-3.5-flash-medium': 'Gemini 3.5 Flash (Medium)',
|
|
'gemini-3.5-flash-high': 'Gemini 3.5 Flash (High)',
|
|
'gemini-3.5-flash-low': 'Gemini 3.5 Flash (Low)',
|
|
'gemini-3.1-pro-low': 'Gemini 3.1 Pro (Low)',
|
|
'gemini-3.1-pro-high': 'Gemini 3.1 Pro (High)',
|
|
'claude-sonnet-4-6': 'Claude Sonnet 4.6 (Thinking)',
|
|
'claude-opus-4-6-thinking': 'Claude Opus 4.6 (Thinking)',
|
|
'gpt-oss-120b-medium': 'GPT-OSS 120B (Medium)',
|
|
} as const
|
|
|
|
export type AgyModelPreset = keyof typeof AGY_MODEL_LABELS
|
|
export const AGY_MODEL_PRESETS = Object.keys(AGY_MODEL_LABELS) as AgyModelPreset[]
|
|
|
|
export function getAgyModelLabel(model: string): string | null {
|
|
const trimmedModel = model.trim()
|
|
if (!trimmedModel) return null
|
|
return AGY_MODEL_LABELS[trimmedModel as AgyModelPreset] ?? null
|
|
}
|
|
|
|
export function isClaudeModelPreset(model: string | null | undefined): model is ClaudeModelPreset {
|
|
return typeof model === 'string' && Object.hasOwn(CLAUDE_MODEL_LABELS, model)
|
|
}
|
|
|
|
export function getClaudeModelLabel(model: string): string | null {
|
|
const trimmedModel = model.trim()
|
|
if (!trimmedModel) {
|
|
return null
|
|
}
|
|
|
|
return CLAUDE_MODEL_LABELS[trimmedModel as ClaudeModelPreset] ?? null
|
|
}
|
|
|
|
/**
|
|
* A Claude model the user can pick.
|
|
*
|
|
* `value` is stored on the session and passed to `claude --model` unchanged.
|
|
* An alias (`opus`) is resolved by the CLI each time the session's claude
|
|
* process starts, so the session moves to a newer model on its next start;
|
|
* a full id (`claude-opus-4-7`) stays on that model.
|
|
*/
|
|
export type ClaudeModelChoice = {
|
|
value: string
|
|
label: string
|
|
/** `main` is each family's newest model; `other` is every earlier version. */
|
|
section: 'main' | 'other'
|
|
}
|
|
|
|
/** `opus`, `opus[1m]`, `claude-opus-4-7` → `opus`. */
|
|
function getClaudeModelFamily(value: string): string {
|
|
return value.replace(/^claude-/, '').split(/[-[]/)[0] ?? value
|
|
}
|
|
|
|
/**
|
|
* Picker choices for a catalog reported by the `claude` CLI: its own `/model`
|
|
* rows, with its display names, split the way Claude Code's model sheet
|
|
* splits them. The CLI lists each family's newest model before its earlier
|
|
* versions, so the first row of a family is `main` and later ones are
|
|
* `other`; within each section the CLI order is kept. The `default` row is
|
|
* omitted; callers represent it as "no model".
|
|
*/
|
|
export function getClaudeModelChoices(catalog: readonly ClaudeModelSummary[]): ClaudeModelChoice[] {
|
|
const families = new Set<string>()
|
|
const choices: ClaudeModelChoice[] = catalog
|
|
.filter((model) => model.value !== 'default')
|
|
.map((model) => {
|
|
const family = getClaudeModelFamily(model.value)
|
|
const section = families.has(family) ? 'other' : 'main'
|
|
families.add(family)
|
|
return { value: model.value, label: model.displayName ?? model.value, section }
|
|
})
|
|
return [
|
|
...choices.filter((choice) => choice.section === 'main'),
|
|
...choices.filter((choice) => choice.section === 'other'),
|
|
]
|
|
}
|
|
|
|
/**
|
|
* Effort levels the CLI offers for `model` (`null` = the session default).
|
|
* Undefined when the catalog is missing or does not list the model.
|
|
*/
|
|
export function getClaudeEffortLevelsForModel(
|
|
model: string | null,
|
|
catalog: readonly ClaudeModelSummary[] | null | undefined
|
|
): readonly string[] | undefined {
|
|
if (!catalog) {
|
|
return undefined
|
|
}
|
|
if (model === null) {
|
|
return catalog.find((row) => row.value === 'default')?.effortLevels
|
|
}
|
|
return catalog.find((row) => row.value === model)?.effortLevels
|
|
}
|
|
|
|
/**
|
|
* The effort to keep when switching to `model`: an effort the model does not
|
|
* offer is cleared rather than left for the CLI to reject or ignore. Unknown
|
|
* support keeps the current effort.
|
|
*/
|
|
export function resolveClaudeEffortForModel(
|
|
effort: string | null,
|
|
model: string | null,
|
|
catalog: readonly ClaudeModelSummary[] | null | undefined
|
|
): string | null {
|
|
const levels = getClaudeEffortLevelsForModel(model, catalog)
|
|
if (effort === null || levels === undefined || levels.includes(effort)) {
|
|
return effort
|
|
}
|
|
return null
|
|
}
|