Merge pull request #1713 from toxeh/fix/model-select-provider-isolation

fix(model-select): provider-isolate label and icon resolution
This commit is contained in:
朱昆鹏
2026-08-22 01:35:09 +08:00
committed by GitHub
2 changed files with 60 additions and 2 deletions
@@ -238,4 +238,53 @@ describe('ModelSelect', () => {
const pinnedSection = screen.getByTestId('model-section-__pinned__');
expect(pinnedSection.textContent).toContain('deepseek/Deepseek-V4-Flash-Free');
});
// Third-party catalogs expose models whose ids collide with the claude-*
// mapping slots. Non-claude providers must render catalog labels verbatim.
it('非 Claude 提供商应原样显示目录标签,不受 Claude 模型映射影响', () => {
localStorage.setItem(
STORAGE_KEYS.CLAUDE_MODEL_MAPPING,
JSON.stringify({ sonnet: 'glm-4' }),
);
render(
<ModelSelect
value={sonnetModel.id}
onChange={vi.fn()}
models={[sonnetModel]}
currentProvider="opencode"
/>,
);
expect(screen.getByRole('button').textContent).toContain('Sonnet 4.6');
expect(screen.getByRole('button').textContent).not.toContain('glm-4');
});
it('同一模型 ID 切换到非 Claude 提供商后不应沿用 Claude 映射标签', () => {
localStorage.setItem(
STORAGE_KEYS.CLAUDE_MODEL_MAPPING,
JSON.stringify({ sonnet: 'glm-4' }),
);
const { rerender } = render(
<ModelSelect
value={sonnetModel.id}
onChange={vi.fn()}
models={[sonnetModel]}
currentProvider="claude"
/>,
);
expect(screen.getByRole('button').textContent).toContain('glm-4');
rerender(
<ModelSelect
value={sonnetModel.id}
onChange={vi.fn()}
models={[sonnetModel]}
currentProvider="opencode"
/>,
);
expect(screen.getByRole('button').textContent).toContain('Sonnet 4.6');
expect(screen.getByRole('button').textContent).not.toContain('glm-4');
});
});
@@ -200,6 +200,15 @@ export const ModelSelect = ({ value, onChange, models = AVAILABLE_MODELS, curren
};
const getModelLabel = (model: ModelInfo, show1MContext = false): string => {
// The Anthropic slot maps below (MODEL_ID_TO_MAPPING_KEY, DEFAULT_MODEL_MAP,
// MODEL_LABEL_KEYS) are keyed by claude-* ids. Third-party catalogs (agy, CLI
// providers) expose models whose ids collide with those slots (e.g.
// claude-sonnet-4-6). For any non-claude provider, render the catalog label
// verbatim — never let the Claude model mapping override catalog labels.
if (currentProvider !== 'claude') {
return append1MContextSuffix(model.label ?? '', model.id, show1MContext);
}
const mappingKey = MODEL_ID_TO_MAPPING_KEY[model.id];
if (mappingKey) {
const mappedName = resolveMappedModelName(mappingKey, modelMapping);
@@ -337,7 +346,7 @@ export const ModelSelect = ({ value, onChange, models = AVAILABLE_MODELS, curren
>
<ProviderModelIcon
providerId={currentProvider}
modelId={resolveModelIdForIcon(currentModel.id, modelMapping, MODEL_ID_TO_MAPPING_KEY)}
modelId={resolveModelIdForIcon(currentModel.id, currentProvider === 'claude' ? modelMapping : {}, MODEL_ID_TO_MAPPING_KEY)}
size={12}
colored
/>
@@ -416,7 +425,7 @@ export const ModelSelect = ({ value, onChange, models = AVAILABLE_MODELS, curren
>
<ProviderModelIcon
providerId={currentProvider}
modelId={resolveModelIdForIcon(model.id, modelMapping, MODEL_ID_TO_MAPPING_KEY)}
modelId={resolveModelIdForIcon(model.id, currentProvider === 'claude' ? modelMapping : {}, MODEL_ID_TO_MAPPING_KEY)}
size={16}
colored
/>