mirror of
https://github.com/tiennm99/ccs.git
synced 2026-10-05 10:13:12 +00:00
feat: add Sonnet 5 to Claude provider catalog
This commit is contained in:
1 parent
631a515986
commit
c8a4b42990
6 files changed
+84
-16
No files matched your search
@@ -132,7 +132,19 @@ describe('Model Catalog', () => {
|
||||
});
|
||||
it('has correct default model', () => {
|
||||
const { MODEL_CATALOG } = modelCatalog;
|
||||
assert.strictEqual(MODEL_CATALOG.claude.defaultModel, 'claude-sonnet-4-6');
|
||||
assert.strictEqual(MODEL_CATALOG.claude.defaultModel, 'claude-sonnet-5');
|
||||
});
|
||||
|
||||
it('includes Claude Sonnet 5 with adaptive levels and extended context', () => {
|
||||
const { MODEL_CATALOG } = modelCatalog;
|
||||
const sonnet = MODEL_CATALOG.claude.models.find((m) => m.id === 'claude-sonnet-5');
|
||||
assert(sonnet, 'Should include Claude Sonnet 5');
|
||||
assert.strictEqual(sonnet.name, 'Claude Sonnet 5');
|
||||
assert.strictEqual(sonnet.thinking.type, 'levels');
|
||||
assert.deepStrictEqual(sonnet.thinking.levels, ['low', 'medium', 'high', 'xhigh', 'max']);
|
||||
assert.strictEqual(sonnet.thinking.maxLevel, 'max');
|
||||
assert.strictEqual(sonnet.nativeImageInput, true);
|
||||
assert.strictEqual(sonnet.extendedContext, true);
|
||||
});
|
||||
|
||||
it('includes Claude Sonnet 4.6', () => {
|
||||
|
||||
@@ -69,6 +69,13 @@ describe('Thinking Validator', () => {
|
||||
expect(result.warning).toBeUndefined();
|
||||
});
|
||||
|
||||
it('should treat max as a distinct top tier on Claude Sonnet 5', () => {
|
||||
const result = validateThinking('claude', 'claude-sonnet-5', 'max');
|
||||
expect(result.valid).toBe(true);
|
||||
expect(result.value).toBe('max');
|
||||
expect(result.warning).toBeUndefined();
|
||||
});
|
||||
|
||||
it('should still alias max -> xhigh for models without a max level (backcompat)', () => {
|
||||
// Codex catalog uses ['low','medium','high','xhigh'] with maxLevel 'xhigh'.
|
||||
// User input "max" should map down to xhigh rather than be rejected.
|
||||
|
||||
@@ -367,8 +367,22 @@ export const MODEL_CATALOG: Partial<Record<CLIProxyProvider, ProviderCatalog>> =
|
||||
claude: {
|
||||
provider: 'claude',
|
||||
displayName: 'Claude (Anthropic)',
|
||||
defaultModel: 'claude-sonnet-4-6',
|
||||
defaultModel: 'claude-sonnet-5',
|
||||
models: [
|
||||
{
|
||||
id: 'claude-sonnet-5',
|
||||
name: 'Claude Sonnet 5',
|
||||
description: 'Latest Sonnet model',
|
||||
nativeImageInput: true,
|
||||
// Sonnet 5 uses adaptive thinking; manual budget_tokens is rejected with 400.
|
||||
thinking: {
|
||||
type: 'levels',
|
||||
levels: ['low', 'medium', 'high', 'xhigh', 'max'],
|
||||
maxLevel: 'max',
|
||||
dynamicAllowed: true,
|
||||
},
|
||||
extendedContext: true,
|
||||
},
|
||||
{
|
||||
id: 'claude-fable-5',
|
||||
name: 'Claude Fable 5',
|
||||
|
||||
@@ -854,8 +854,20 @@ export const MODEL_CATALOGS: Record<string, ProviderCatalog> = {
|
||||
claude: {
|
||||
provider: 'claude',
|
||||
displayName: 'Claude (Anthropic)',
|
||||
defaultModel: 'claude-sonnet-4-6',
|
||||
defaultModel: 'claude-sonnet-5',
|
||||
models: [
|
||||
{
|
||||
id: 'claude-sonnet-5',
|
||||
name: 'Claude Sonnet 5',
|
||||
description: 'Latest Sonnet model',
|
||||
extendedContext: true,
|
||||
presetMapping: {
|
||||
default: 'claude-sonnet-5',
|
||||
opus: 'claude-opus-4-8',
|
||||
sonnet: 'claude-sonnet-5',
|
||||
haiku: 'claude-haiku-4-5-20251001',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'claude-fable-5',
|
||||
name: 'Claude Fable 5',
|
||||
@@ -864,7 +876,7 @@ export const MODEL_CATALOGS: Record<string, ProviderCatalog> = {
|
||||
presetMapping: {
|
||||
default: 'claude-fable-5',
|
||||
opus: 'claude-fable-5',
|
||||
sonnet: 'claude-sonnet-4-6',
|
||||
sonnet: 'claude-sonnet-5',
|
||||
haiku: 'claude-haiku-4-5-20251001',
|
||||
},
|
||||
},
|
||||
@@ -876,7 +888,7 @@ export const MODEL_CATALOGS: Record<string, ProviderCatalog> = {
|
||||
presetMapping: {
|
||||
default: 'claude-opus-4-8',
|
||||
opus: 'claude-opus-4-8',
|
||||
sonnet: 'claude-sonnet-4-6',
|
||||
sonnet: 'claude-sonnet-5',
|
||||
haiku: 'claude-haiku-4-5-20251001',
|
||||
},
|
||||
},
|
||||
@@ -888,7 +900,7 @@ export const MODEL_CATALOGS: Record<string, ProviderCatalog> = {
|
||||
presetMapping: {
|
||||
default: 'claude-opus-4-7',
|
||||
opus: 'claude-opus-4-7',
|
||||
sonnet: 'claude-sonnet-4-6',
|
||||
sonnet: 'claude-sonnet-5',
|
||||
haiku: 'claude-haiku-4-5-20251001',
|
||||
},
|
||||
},
|
||||
|
||||
@@ -70,7 +70,7 @@ describe('FlexibleModelSelector', () => {
|
||||
expect(screen.queryByText(/All Models \(/i)).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('surfaces Claude Opus 4.7 in the curated Claude picker', async () => {
|
||||
it('surfaces Claude Sonnet 5 in the curated Claude picker', async () => {
|
||||
render(
|
||||
<FlexibleModelSelector
|
||||
label="Primary model"
|
||||
@@ -83,8 +83,8 @@ describe('FlexibleModelSelector', () => {
|
||||
|
||||
await userEvent.click(screen.getByRole('button', { name: /select model/i }));
|
||||
|
||||
expect(screen.getByRole('option', { name: /claude-opus-4-7/i })).toBeInTheDocument();
|
||||
expect(screen.getByRole('option', { name: /claude-opus-4-6/i })).toBeInTheDocument();
|
||||
expect(screen.getByRole('option', { name: /claude-sonnet-5/i })).toBeInTheDocument();
|
||||
expect(screen.getByRole('option', { name: /claude-opus-4-8/i })).toBeInTheDocument();
|
||||
expect(screen.getByRole('option', { name: /claude-sonnet-4-6/i })).toBeInTheDocument();
|
||||
});
|
||||
|
||||
@@ -129,6 +129,28 @@ describe('FlexibleModelSelector', () => {
|
||||
expect(onChange).toHaveBeenCalledWith('claude-sonnet-6');
|
||||
});
|
||||
|
||||
it('lets users enter a future direct Claude model ID from the CLIProxy picker', async () => {
|
||||
const onChange = vi.fn();
|
||||
|
||||
render(
|
||||
<FlexibleModelSelector
|
||||
label="Primary model"
|
||||
value={undefined}
|
||||
onChange={onChange}
|
||||
catalog={MODEL_CATALOGS.claude}
|
||||
allModels={[]}
|
||||
/>
|
||||
);
|
||||
|
||||
await userEvent.click(screen.getByRole('button', { name: /select model/i }));
|
||||
await userEvent.type(await screen.findByPlaceholderText('Search models...'), 'claude-sonnet-6');
|
||||
|
||||
expect(screen.getAllByText('Custom model').length).toBeGreaterThan(0);
|
||||
await userEvent.click(screen.getByRole('option', { name: /claude-sonnet-6/i }));
|
||||
|
||||
expect(onChange).toHaveBeenCalledWith('claude-sonnet-6');
|
||||
});
|
||||
|
||||
it('preserves a filtered legacy value under the current-value fallback group', async () => {
|
||||
render(
|
||||
<FlexibleModelSelector
|
||||
|
||||
@@ -16,11 +16,12 @@ describe('claude preset utils', () => {
|
||||
vi.restoreAllMocks();
|
||||
});
|
||||
|
||||
it('keeps the claude catalog default on Sonnet 4.6 while exposing Opus 4.7/4.8 and Fable 5', () => {
|
||||
it('keeps the claude catalog default on Sonnet 5 while exposing recent Claude models', () => {
|
||||
const claudeCatalog = MODEL_CATALOGS.claude;
|
||||
const ids = claudeCatalog.models.map((model) => model.id);
|
||||
|
||||
expect(claudeCatalog.defaultModel).toBe('claude-sonnet-4-6');
|
||||
expect(claudeCatalog.defaultModel).toBe('claude-sonnet-5');
|
||||
expect(ids).toContain('claude-sonnet-5');
|
||||
expect(ids).toContain('claude-fable-5');
|
||||
expect(ids).toContain('claude-opus-4-8');
|
||||
expect(ids).toContain('claude-opus-4-7');
|
||||
@@ -50,15 +51,15 @@ describe('claude preset utils', () => {
|
||||
|
||||
const result = await applyDefaultPreset('claude');
|
||||
|
||||
expect(result).toEqual({ success: true, presetName: 'Claude Sonnet 4.6' });
|
||||
expect(result).toEqual({ success: true, presetName: 'Claude Sonnet 5' });
|
||||
|
||||
const [, requestInit] = fetchMock.mock.calls[2] ?? [];
|
||||
const body = JSON.parse(String(requestInit?.body));
|
||||
|
||||
expect(body.settings.env).toMatchObject({
|
||||
ANTHROPIC_MODEL: 'claude-sonnet-4-6',
|
||||
ANTHROPIC_DEFAULT_OPUS_MODEL: 'claude-opus-4-6',
|
||||
ANTHROPIC_DEFAULT_SONNET_MODEL: 'claude-sonnet-4-6',
|
||||
ANTHROPIC_MODEL: 'claude-sonnet-5',
|
||||
ANTHROPIC_DEFAULT_OPUS_MODEL: 'claude-opus-4-8',
|
||||
ANTHROPIC_DEFAULT_SONNET_MODEL: 'claude-sonnet-5',
|
||||
ANTHROPIC_DEFAULT_HAIKU_MODEL: 'claude-haiku-4-5-20251001',
|
||||
});
|
||||
});
|
||||
@@ -76,7 +77,7 @@ describe('claude preset utils', () => {
|
||||
|
||||
const result = await applyDefaultPreset('claude', undefined, MODEL_CATALOGS.claude);
|
||||
|
||||
expect(result).toEqual({ success: true, presetName: 'Claude Sonnet 4.6' });
|
||||
expect(result).toEqual({ success: true, presetName: 'Claude Sonnet 5' });
|
||||
expect(fetchMock).toHaveBeenCalledTimes(2);
|
||||
expect(fetchMock).toHaveBeenNthCalledWith(1, '/api/settings/auth/tokens/raw');
|
||||
expect(fetchMock).toHaveBeenNthCalledWith(
|
||||
|
||||
Reference in new issue
Block a user