feat(cliproxy): add warning for broken claude proxy models on agy

- add broken flag to model catalog for models with known issues
- display [BROKEN] badge in model selection and config display
- show warning message when starting with a broken model
- link to tracking issue: CLIProxyAPI#415

Claude proxy models (gemini-claude-*) currently fail with tool calls
due to upstream CLIProxyAPI bug. Warning helps users know to switch
to gemini-3-pro-preview until fixed.
This commit is contained in:
kaitranntt
2025-12-04 06:22:38 -05:00
parent 43e45c157c
commit 0e11426daa
3 changed files with 50 additions and 5 deletions
+20 -3
View File
@@ -26,8 +26,8 @@ import {
} from './config-generator';
import { isAuthenticated } from './auth-handler';
import { CLIProxyProvider, ExecutorConfig } from './types';
import { configureProviderModel } from './model-config';
import { supportsModelConfig } from './model-catalog';
import { configureProviderModel, getCurrentModel } from './model-config';
import { supportsModelConfig, isModelBroken, getModelIssueUrl, findModel } from './model-catalog';
/** Default executor configuration */
const DEFAULT_CONFIG: ExecutorConfig = {
@@ -170,7 +170,24 @@ export async function execClaudeWithCLIProxy(
await configureProviderModel(provider, false); // false = only if not configured
}
// 5. Ensure user settings file exists (creates from defaults if not)
// 5. Check for known broken models and warn user
const currentModel = getCurrentModel(provider);
if (currentModel && isModelBroken(provider, currentModel)) {
const modelEntry = findModel(provider, currentModel);
const issueUrl = getModelIssueUrl(provider, currentModel);
console.error('');
console.error(
`[!] Warning: ${modelEntry?.name || currentModel} has known issues with Claude Code`
);
console.error(' Tool calls will fail. Use "gemini-3-pro-preview" instead.');
if (issueUrl) {
console.error(` Tracking: ${issueUrl}`);
}
console.error(` Run "ccs ${provider} --config" to change model.`);
console.error('');
}
// 6. Ensure user settings file exists (creates from defaults if not)
ensureProviderSettings(provider);
// 6. Generate config file
+26
View File
@@ -19,6 +19,10 @@ export interface ModelEntry {
tier?: 'free' | 'paid';
/** Optional description for the model */
description?: string;
/** Model has known issues - show warning when selected */
broken?: boolean;
/** Issue URL for broken models */
issueUrl?: string;
}
/**
@@ -46,16 +50,22 @@ export const MODEL_CATALOG: Partial<Record<CLIProxyProvider, ProviderCatalog>> =
id: 'gemini-claude-opus-4-5-thinking',
name: 'Claude Opus 4.5 Thinking',
description: 'Most capable, extended thinking',
broken: true,
issueUrl: 'https://github.com/router-for-me/CLIProxyAPI/issues/415',
},
{
id: 'gemini-claude-sonnet-4-5-thinking',
name: 'Claude Sonnet 4.5 Thinking',
description: 'Balanced with extended thinking',
broken: true,
issueUrl: 'https://github.com/router-for-me/CLIProxyAPI/issues/415',
},
{
id: 'gemini-claude-sonnet-4-5',
name: 'Claude Sonnet 4.5',
description: 'Fast and capable',
broken: true,
issueUrl: 'https://github.com/router-for-me/CLIProxyAPI/issues/415',
},
{
id: 'gemini-3-pro-preview',
@@ -107,3 +117,19 @@ export function findModel(provider: CLIProxyProvider, modelId: string): ModelEnt
if (!catalog) return undefined;
return catalog.models.find((m) => m.id === modelId);
}
/**
* Check if model has known issues
*/
export function isModelBroken(provider: CLIProxyProvider, modelId: string): boolean {
const model = findModel(provider, modelId);
return model?.broken === true;
}
/**
* Get issue URL for broken model
*/
export function getModelIssueUrl(provider: CLIProxyProvider, modelId: string): string | undefined {
const model = findModel(provider, modelId);
return model?.issueUrl;
}
+4 -2
View File
@@ -45,7 +45,8 @@ export function getCurrentModel(provider: CLIProxyProvider): string | undefined
function formatModelOption(model: ModelEntry): string {
// Tier badge: clarify that "paid" means paid Google account (not free tier)
const tierBadge = model.tier === 'paid' ? color(' [Paid Tier]', 'warning') : '';
return `${model.name}${tierBadge}`;
const brokenBadge = model.broken ? color(' [BROKEN]', 'error') : '';
return `${model.name}${tierBadge}${brokenBadge}`;
}
/**
@@ -55,8 +56,9 @@ function formatModelDetailed(model: ModelEntry, isCurrent: boolean): string {
const marker = isCurrent ? color('>', 'success') : ' ';
const name = isCurrent ? bold(model.name) : model.name;
const tierBadge = model.tier === 'paid' ? color(' [Paid Tier]', 'warning') : '';
const brokenBadge = model.broken ? color(' [BROKEN]', 'error') : '';
const desc = model.description ? dim(` - ${model.description}`) : '';
return ` ${marker} ${name}${tierBadge}${desc}`;
return ` ${marker} ${name}${tierBadge}${brokenBadge}${desc}`;
}
/**