Files
ccs/ui/src/lib/preset-utils.ts
T
Tam Nhu Tran 564ed3def7 fix(ui): reuse cached cliproxy catalogs for presets
- let applyDefaultPreset accept a caller-provided provider catalog

- pass cached catalog data from cliproxy and quick-setup flows

- add regression coverage for skipping the extra catalog fetch
2026-04-08 17:52:45 -04:00

105 lines
3.3 KiB
TypeScript

/**
* Preset Utilities
* Shared functions for applying default presets to provider settings
*/
import type { CliproxyProviderCatalog } from './api-client';
import { MODEL_CATALOGS } from './model-catalogs';
import { buildUiCatalogs } from './model-catalogs';
import { CLIPROXY_DEFAULT_PORT } from './default-ports';
export { CLIPROXY_DEFAULT_PORT } from './default-ports';
/** Default fallback API key if fetch fails */
const DEFAULT_API_KEY = 'ccs-internal-managed';
/**
* Fetch effective API key from backend
* Falls back to default if fetch fails
*/
async function fetchEffectiveApiKey(): Promise<string> {
try {
const response = await fetch('/api/settings/auth/tokens/raw');
if (!response.ok) return DEFAULT_API_KEY;
const data = await response.json();
return data?.apiKey?.value ?? DEFAULT_API_KEY;
} catch {
return DEFAULT_API_KEY;
}
}
async function fetchProviderCatalog(provider: string, catalog?: CliproxyProviderCatalog) {
if (catalog) {
return catalog;
}
try {
const response = await fetch('/api/cliproxy/catalog');
if (!response.ok) {
return MODEL_CATALOGS[provider];
}
const data = (await response.json()) as {
catalogs?: Partial<Record<string, (typeof MODEL_CATALOGS)[string]>>;
};
return buildUiCatalogs(data.catalogs)[provider] ?? MODEL_CATALOGS[provider];
} catch {
return MODEL_CATALOGS[provider];
}
}
/**
* Apply default preset for a provider to its settings
* Uses the catalog default model's preset mapping or falls back to using defaultModel for all tiers
*
* @param provider - The provider ID (e.g., 'gemini', 'codex', 'agy')
* @param port - Optional custom port (defaults to CLIPROXY_DEFAULT_PORT)
* @returns Object with success status and applied preset name
*/
export async function applyDefaultPreset(
provider: string,
port?: number,
catalog?: CliproxyProviderCatalog
): Promise<{ success: boolean; presetName?: string }> {
const resolvedCatalog = await fetchProviderCatalog(provider, catalog);
if (!resolvedCatalog) return { success: false };
const defaultModelEntry =
resolvedCatalog.models.find((model) => model.id === resolvedCatalog.defaultModel) ||
resolvedCatalog.models[0];
const mapping = defaultModelEntry?.presetMapping || {
default: resolvedCatalog.defaultModel,
opus: resolvedCatalog.defaultModel,
sonnet: resolvedCatalog.defaultModel,
haiku: resolvedCatalog.defaultModel,
};
// Fetch effective API key (respects user customization)
const effectiveApiKey = await fetchEffectiveApiKey();
const effectivePort = port ?? CLIPROXY_DEFAULT_PORT;
const settings = {
env: {
ANTHROPIC_BASE_URL: `http://127.0.0.1:${effectivePort}/api/provider/${provider}`,
ANTHROPIC_AUTH_TOKEN: effectiveApiKey,
ANTHROPIC_MODEL: mapping.default,
ANTHROPIC_DEFAULT_OPUS_MODEL: mapping.opus,
ANTHROPIC_DEFAULT_SONNET_MODEL: mapping.sonnet,
ANTHROPIC_DEFAULT_HAIKU_MODEL: mapping.haiku,
},
};
try {
const res = await fetch(`/api/settings/${provider}`, {
method: 'PUT',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ settings }),
});
return {
success: res.ok,
presetName: defaultModelEntry?.name || resolvedCatalog.defaultModel,
};
} catch {
return { success: false };
}
}