mirror of
https://github.com/tiennm99/ccs.git
synced 2026-07-20 00:17:38 +00:00
fix: keep live catalog defaults visible
This commit is contained in:
@@ -55,6 +55,18 @@ describe('model-catalog compatibility lookups', () => {
|
|||||||
expect(catalog?.models.map((model) => model.id)).toEqual(['gemini-2.5-pro']);
|
expect(catalog?.models.map((model) => model.id)).toEqual(['gemini-2.5-pro']);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('falls back to a visible live model when the static default is absent', () => {
|
||||||
|
const catalog = mergeCatalog('gemini', [
|
||||||
|
{
|
||||||
|
id: 'gemini-live-only',
|
||||||
|
display_name: 'Gemini Live Only',
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
|
||||||
|
expect(catalog?.defaultModel).toBe('gemini-live-only');
|
||||||
|
expect(catalog?.models.map((model) => model.id)).toContain(catalog?.defaultModel);
|
||||||
|
});
|
||||||
|
|
||||||
it('preserves static maxLevel when live thinking metadata omits it', () => {
|
it('preserves static maxLevel when live thinking metadata omits it', () => {
|
||||||
const catalog = mergeCatalog('claude', [
|
const catalog = mergeCatalog('claude', [
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -483,9 +483,8 @@ export async function reconcileExhaustedRotationAccounts(
|
|||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
const { pauseAccountForQuotaCooldown, restoreExpiredQuotaPauses } = await import(
|
const { pauseAccountForQuotaCooldown, restoreExpiredQuotaPauses } =
|
||||||
'../accounts/account-safety'
|
await import('../accounts/account-safety');
|
||||||
);
|
|
||||||
restoreExpiredQuotaPauses();
|
restoreExpiredQuotaPauses();
|
||||||
|
|
||||||
const config = loadOrCreateUnifiedConfig();
|
const config = loadOrCreateUnifiedConfig();
|
||||||
@@ -593,9 +592,8 @@ export async function preflightCheck(provider: CLIProxyProvider): Promise<Prefli
|
|||||||
return { proceed: true, accountId: defaultAccount?.id || '' };
|
return { proceed: true, accountId: defaultAccount?.id || '' };
|
||||||
}
|
}
|
||||||
|
|
||||||
const { pauseAccountForQuotaCooldown, restoreExpiredQuotaPauses } = await import(
|
const { pauseAccountForQuotaCooldown, restoreExpiredQuotaPauses } =
|
||||||
'../accounts/account-safety'
|
await import('../accounts/account-safety');
|
||||||
);
|
|
||||||
restoreExpiredQuotaPauses();
|
restoreExpiredQuotaPauses();
|
||||||
|
|
||||||
const config = loadOrCreateUnifiedConfig();
|
const config = loadOrCreateUnifiedConfig();
|
||||||
|
|||||||
@@ -249,7 +249,6 @@ export function mergeCatalog(
|
|||||||
if (!staticCatalog && filteredRemoteModels.length === 0) return undefined;
|
if (!staticCatalog && filteredRemoteModels.length === 0) return undefined;
|
||||||
|
|
||||||
const displayName = staticCatalog?.displayName || provider;
|
const displayName = staticCatalog?.displayName || provider;
|
||||||
const defaultModel = staticCatalog?.defaultModel || (filteredRemoteModels[0]?.id ?? '');
|
|
||||||
|
|
||||||
// Build map of static models by lowercase ID for fast lookup
|
// Build map of static models by lowercase ID for fast lookup
|
||||||
const staticMap = new Map<string, ModelEntry>();
|
const staticMap = new Map<string, ModelEntry>();
|
||||||
@@ -260,14 +259,11 @@ export function mergeCatalog(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Process remote models: merge with static entries
|
// Process remote models: merge with static entries
|
||||||
const mergedIds = new Set<string>();
|
|
||||||
const mergedModels: ModelEntry[] = [];
|
const mergedModels: ModelEntry[] = [];
|
||||||
|
|
||||||
for (const remote of filteredRemoteModels) {
|
for (const remote of filteredRemoteModels) {
|
||||||
const remoteEntry = mapRemoteToModelEntry(remote);
|
const remoteEntry = mapRemoteToModelEntry(remote);
|
||||||
const staticEntry = staticMap.get(remote.id.toLowerCase());
|
const staticEntry = staticMap.get(remote.id.toLowerCase());
|
||||||
mergedIds.add(remote.id.toLowerCase());
|
|
||||||
|
|
||||||
if (staticEntry) {
|
if (staticEntry) {
|
||||||
const mergedThinking = remoteEntry.thinking
|
const mergedThinking = remoteEntry.thinking
|
||||||
? {
|
? {
|
||||||
@@ -292,6 +288,12 @@ export function mergeCatalog(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const staticDefaultModel = staticCatalog?.defaultModel;
|
||||||
|
const hasStaticDefaultModel =
|
||||||
|
typeof staticDefaultModel === 'string' &&
|
||||||
|
mergedModels.some((model) => model.id.toLowerCase() === staticDefaultModel.toLowerCase());
|
||||||
|
const defaultModel = hasStaticDefaultModel ? staticDefaultModel : (mergedModels[0]?.id ?? '');
|
||||||
|
|
||||||
return {
|
return {
|
||||||
provider,
|
provider,
|
||||||
displayName,
|
displayName,
|
||||||
|
|||||||
@@ -112,9 +112,8 @@ export async function runImageAnalysisCheck(results: HealthCheck): Promise<void>
|
|||||||
* Fix image analysis configuration issues
|
* Fix image analysis configuration issues
|
||||||
*/
|
*/
|
||||||
export async function fixImageAnalysisConfig(): Promise<boolean> {
|
export async function fixImageAnalysisConfig(): Promise<boolean> {
|
||||||
const { updateConfig, loadOrCreateUnifiedConfig } = await import(
|
const { updateConfig, loadOrCreateUnifiedConfig } =
|
||||||
'../../config/config-loader-facade'
|
await import('../../config/config-loader-facade');
|
||||||
);
|
|
||||||
|
|
||||||
const config = loadOrCreateUnifiedConfig();
|
const config = loadOrCreateUnifiedConfig();
|
||||||
let fixed = false;
|
let fixed = false;
|
||||||
|
|||||||
@@ -792,11 +792,19 @@ export function buildUiCatalog(
|
|||||||
availableModels.some(
|
availableModels.some(
|
||||||
(model) => normalizeModelId(model.id) === normalizeModelId(fallbackDefaultModel)
|
(model) => normalizeModelId(model.id) === normalizeModelId(fallbackDefaultModel)
|
||||||
);
|
);
|
||||||
|
const hasLiveDefaultModel = availableModels.some(
|
||||||
|
(model) => normalizeModelId(model.id) === normalizeModelId(liveCatalog.defaultModel)
|
||||||
|
);
|
||||||
|
const defaultModel = hasFallbackDefaultModel
|
||||||
|
? fallbackDefaultModel
|
||||||
|
: hasLiveDefaultModel
|
||||||
|
? liveCatalog.defaultModel
|
||||||
|
: (models[0]?.id ?? '');
|
||||||
|
|
||||||
return {
|
return {
|
||||||
provider: liveCatalog.provider,
|
provider: liveCatalog.provider,
|
||||||
displayName: liveCatalog.displayName || staticCatalog?.displayName || provider,
|
displayName: liveCatalog.displayName || staticCatalog?.displayName || provider,
|
||||||
defaultModel: hasFallbackDefaultModel ? fallbackDefaultModel : liveCatalog.defaultModel,
|
defaultModel,
|
||||||
models,
|
models,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -66,11 +66,12 @@ export async function applyDefaultPreset(
|
|||||||
const defaultModelEntry =
|
const defaultModelEntry =
|
||||||
resolvedCatalog.models.find((model) => model.id === resolvedCatalog.defaultModel) ||
|
resolvedCatalog.models.find((model) => model.id === resolvedCatalog.defaultModel) ||
|
||||||
resolvedCatalog.models[0];
|
resolvedCatalog.models[0];
|
||||||
|
const selectedModelId = defaultModelEntry?.id ?? resolvedCatalog.defaultModel;
|
||||||
const mapping = defaultModelEntry?.presetMapping || {
|
const mapping = defaultModelEntry?.presetMapping || {
|
||||||
default: resolvedCatalog.defaultModel,
|
default: selectedModelId,
|
||||||
opus: resolvedCatalog.defaultModel,
|
opus: selectedModelId,
|
||||||
sonnet: resolvedCatalog.defaultModel,
|
sonnet: selectedModelId,
|
||||||
haiku: resolvedCatalog.defaultModel,
|
haiku: selectedModelId,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Fetch effective API key (respects user customization)
|
// Fetch effective API key (respects user customization)
|
||||||
|
|||||||
@@ -116,6 +116,55 @@ describe('claude preset utils', () => {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('builds UI catalogs with a visible default when the live default is stale', () => {
|
||||||
|
const liveCatalogs = {
|
||||||
|
gemini: {
|
||||||
|
provider: 'gemini',
|
||||||
|
displayName: 'Gemini',
|
||||||
|
defaultModel: 'gemini-2.5-pro',
|
||||||
|
models: [{ id: 'gemini-live-only', name: 'Gemini Live Only' }],
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const catalogs = buildUiCatalogs(liveCatalogs);
|
||||||
|
|
||||||
|
expect(catalogs.gemini?.defaultModel).toBe('gemini-live-only');
|
||||||
|
expect(catalogs.gemini?.models.map((model) => model.id)).toContain(
|
||||||
|
catalogs.gemini?.defaultModel
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('uses the selected visible model for quick setup fallback mappings', async () => {
|
||||||
|
const fetchMock = vi
|
||||||
|
.fn()
|
||||||
|
.mockResolvedValueOnce({
|
||||||
|
ok: true,
|
||||||
|
json: async () => ({ apiKey: { value: 'managed-key' } }),
|
||||||
|
})
|
||||||
|
.mockResolvedValueOnce({ ok: true });
|
||||||
|
|
||||||
|
vi.stubGlobal('fetch', fetchMock);
|
||||||
|
|
||||||
|
const result = await applyDefaultPreset('gemini', undefined, {
|
||||||
|
provider: 'gemini',
|
||||||
|
displayName: 'Gemini',
|
||||||
|
defaultModel: 'gemini-2.5-pro',
|
||||||
|
models: [{ id: 'gemini-live-only', name: 'Gemini Live Only' }],
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(result).toEqual({ success: true, presetName: 'Gemini Live Only' });
|
||||||
|
|
||||||
|
const [, requestInit] = fetchMock.mock.calls[1] ?? [];
|
||||||
|
const body = JSON.parse(String(requestInit?.body));
|
||||||
|
|
||||||
|
expect(body.settings.env).toMatchObject({
|
||||||
|
ANTHROPIC_MODEL: 'gemini-live-only',
|
||||||
|
ANTHROPIC_DEFAULT_OPUS_MODEL: 'gemini-live-only',
|
||||||
|
ANTHROPIC_DEFAULT_SONNET_MODEL: 'gemini-live-only',
|
||||||
|
ANTHROPIC_DEFAULT_HAIKU_MODEL: 'gemini-live-only',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
it('keeps Gemini presets on 3.1 Pro while resolving 3/3.1 alias variants', () => {
|
it('keeps Gemini presets on 3.1 Pro while resolving 3/3.1 alias variants', () => {
|
||||||
const geminiCatalog = MODEL_CATALOGS.gemini;
|
const geminiCatalog = MODEL_CATALOGS.gemini;
|
||||||
const latestPro = geminiCatalog.models.find((model) => model.id === 'gemini-3.1-pro-preview');
|
const latestPro = geminiCatalog.models.find((model) => model.id === 'gemini-3.1-pro-preview');
|
||||||
|
|||||||
Reference in New Issue
Block a user