agent-ecosystem/test/renderer/utils/teamModelCatalog.test.ts
2026-05-31 22:20:29 +03:00

317 lines
11 KiB
TypeScript

import {
getVisibleTeamProviderModels,
isAnthropicOneMillionContextTeamModel,
isAnthropicSonnetOneMillionContextTeamModel,
isAnthropicSonnetTeamModel,
} from '@renderer/utils/teamModelCatalog';
import { describe, expect, it } from 'vitest';
describe('teamModelCatalog', () => {
it('filters UI-disabled Codex models from provider badge lists', () => {
expect(
getVisibleTeamProviderModels('codex', [
'gpt-5.4',
'gpt-5.4-mini',
'gpt-5.3-codex',
'gpt-5.3-codex-spark',
'gpt-5.2',
'gpt-5.2-codex',
'gpt-5.1-codex-mini',
'gpt-5.1-codex-max',
])
).toEqual(['gpt-5.4', 'gpt-5.4-mini', 'gpt-5.3-codex', 'gpt-5.2', 'gpt-5.1-codex-max']);
});
it('adds curated Anthropic Opus 4.8 badges when the runtime list only reports legacy Opus variants', () => {
expect(
getVisibleTeamProviderModels('anthropic', [
'claude-haiku-4-5-20251001',
'claude-opus-4-6',
'claude-opus-4-6[1m]',
'claude-sonnet-4-6',
'claude-sonnet-4-6[1m]',
])
).toEqual([
'claude-haiku-4-5-20251001',
'claude-opus-4-8',
'claude-opus-4-8[1m]',
'claude-opus-4-7',
'claude-opus-4-7[1m]',
'claude-opus-4-6',
'claude-opus-4-6[1m]',
'claude-sonnet-4-6',
'claude-sonnet-4-6[1m]',
]);
});
it('orders OpenCode free models before paid models', () => {
expect(
getVisibleTeamProviderModels(
'opencode',
[
'openrouter/deepseek/deepseek-r1',
'openai/gpt-5.4',
'openrouter/openai/gpt-oss-20b:free',
'opencode/big-pickle',
],
{
providerId: 'opencode',
authMethod: 'opencode_managed',
backend: { kind: 'opencode-cli', label: 'OpenCode CLI' },
modelCatalog: {
schemaVersion: 1,
providerId: 'opencode',
source: 'app-server',
status: 'ready',
fetchedAt: '2026-05-12T00:00:00.000Z',
staleAt: '2026-05-12T00:10:00.000Z',
defaultModelId: 'opencode/big-pickle',
defaultLaunchModel: 'opencode/big-pickle',
models: [
{
id: 'opencode/big-pickle',
launchModel: 'opencode/big-pickle',
displayName: 'opencode/big-pickle',
hidden: false,
supportedReasoningEfforts: [],
defaultReasoningEffort: null,
inputModalities: ['text'],
supportsPersonality: true,
isDefault: true,
upgrade: false,
source: 'app-server',
badgeLabel: 'Free',
},
{
id: 'openrouter/openai/gpt-oss-20b:free',
launchModel: 'openrouter/openai/gpt-oss-20b:free',
displayName: 'openrouter/openai/gpt-oss-20b:free',
hidden: false,
supportedReasoningEfforts: [],
defaultReasoningEffort: null,
inputModalities: ['text'],
supportsPersonality: true,
isDefault: false,
upgrade: false,
source: 'app-server',
badgeLabel: 'Free',
},
{
id: 'openai/gpt-5.4',
launchModel: 'openai/gpt-5.4',
displayName: 'openai/gpt-5.4',
hidden: false,
supportedReasoningEfforts: [],
defaultReasoningEffort: null,
inputModalities: ['text'],
supportsPersonality: true,
isDefault: false,
upgrade: false,
source: 'app-server',
badgeLabel: null,
},
{
id: 'openrouter/deepseek/deepseek-r1',
launchModel: 'openrouter/deepseek/deepseek-r1',
displayName: 'openrouter/deepseek/deepseek-r1',
hidden: false,
supportedReasoningEfforts: [],
defaultReasoningEffort: null,
inputModalities: ['text'],
supportsPersonality: true,
isDefault: false,
upgrade: false,
source: 'app-server',
badgeLabel: null,
},
],
diagnostics: {
configReadState: 'ready',
appServerState: 'healthy',
},
},
}
)
).toEqual([
'opencode/big-pickle',
'openrouter/openai/gpt-oss-20b:free',
'openai/gpt-5.4',
'openrouter/deepseek/deepseek-r1',
]);
});
it('orders OpenCode free models by metadata when badge labels are absent', () => {
expect(
getVisibleTeamProviderModels(
'opencode',
[
'openai/gpt-5.4',
'opencode/big-pickle',
'openrouter/openai/gpt-oss-20b',
],
{
providerId: 'opencode',
authMethod: 'opencode_managed',
backend: { kind: 'opencode-cli', label: 'OpenCode CLI' },
modelCatalog: {
schemaVersion: 1,
providerId: 'opencode',
source: 'app-server',
status: 'ready',
fetchedAt: '2026-05-12T00:00:00.000Z',
staleAt: '2026-05-12T00:10:00.000Z',
defaultModelId: 'opencode/big-pickle',
defaultLaunchModel: 'opencode/big-pickle',
models: [
{
id: 'openai/gpt-5.4',
launchModel: 'openai/gpt-5.4',
displayName: 'openai/gpt-5.4',
hidden: false,
supportedReasoningEfforts: [],
defaultReasoningEffort: null,
inputModalities: ['text'],
supportsPersonality: true,
isDefault: false,
upgrade: false,
source: 'app-server',
badgeLabel: null,
metadata: { free: false },
},
{
id: 'openrouter/openai/gpt-oss-20b',
launchModel: 'openrouter/openai/gpt-oss-20b',
displayName: 'openrouter/openai/gpt-oss-20b',
hidden: false,
supportedReasoningEfforts: [],
defaultReasoningEffort: null,
inputModalities: ['text'],
supportsPersonality: true,
isDefault: false,
upgrade: false,
source: 'app-server',
badgeLabel: null,
metadata: { free: true },
},
{
id: 'opencode/big-pickle',
launchModel: 'opencode/big-pickle',
displayName: 'opencode/big-pickle',
hidden: false,
supportedReasoningEfforts: [],
defaultReasoningEffort: null,
inputModalities: ['text'],
supportsPersonality: true,
isDefault: true,
upgrade: false,
source: 'app-server',
badgeLabel: null,
metadata: { free: true },
},
],
diagnostics: {
configReadState: 'ready',
appServerState: 'healthy',
},
},
}
)
).toEqual([
'opencode/big-pickle',
'openrouter/openai/gpt-oss-20b',
'openai/gpt-5.4',
]);
});
it('uses the OpenCode model catalog when the runtime model list is summary-only', () => {
expect(
getVisibleTeamProviderModels('opencode', ['opencode/big-pickle'], {
providerId: 'opencode',
authMethod: 'opencode_managed',
backend: { kind: 'opencode-cli', label: 'OpenCode CLI' },
modelCatalog: {
schemaVersion: 1,
providerId: 'opencode',
source: 'app-server',
status: 'ready',
fetchedAt: '2026-05-12T00:00:00.000Z',
staleAt: '2026-05-12T00:10:00.000Z',
defaultModelId: 'opencode/big-pickle',
defaultLaunchModel: 'opencode/big-pickle',
models: [
{
id: 'openai/gpt-5.4',
launchModel: 'openai/gpt-5.4',
displayName: 'openai/gpt-5.4',
hidden: false,
supportedReasoningEfforts: [],
defaultReasoningEffort: null,
inputModalities: ['text'],
supportsPersonality: true,
isDefault: false,
upgrade: false,
source: 'app-server',
badgeLabel: null,
},
{
id: 'opencode/big-pickle',
launchModel: 'opencode/big-pickle',
displayName: 'opencode/big-pickle',
hidden: false,
supportedReasoningEfforts: [],
defaultReasoningEffort: null,
inputModalities: ['text'],
supportsPersonality: true,
isDefault: true,
upgrade: false,
source: 'app-server',
badgeLabel: 'Free',
},
{
id: 'openrouter/hidden-model',
launchModel: 'openrouter/hidden-model',
displayName: 'openrouter/hidden-model',
hidden: true,
supportedReasoningEfforts: [],
defaultReasoningEffort: null,
inputModalities: ['text'],
supportsPersonality: true,
isDefault: false,
upgrade: false,
source: 'app-server',
badgeLabel: null,
},
],
diagnostics: {
configReadState: 'ready',
appServerState: 'healthy',
},
},
})
).toEqual(['opencode/big-pickle', 'openai/gpt-5.4']);
});
it('detects Sonnet aliases with or without 1M suffix', () => {
expect(isAnthropicSonnetTeamModel('sonnet')).toBe(true);
expect(isAnthropicSonnetTeamModel('sonnet[1m]')).toBe(true);
expect(isAnthropicSonnetTeamModel('claude-sonnet-4-6')).toBe(true);
expect(isAnthropicSonnetTeamModel('claude-sonnet-4-6[1m]')).toBe(true);
expect(isAnthropicSonnetTeamModel('opus')).toBe(false);
expect(isAnthropicSonnetTeamModel('haiku')).toBe(false);
});
it('detects 1M Anthropic selections and native 1M launch ids', () => {
expect(isAnthropicOneMillionContextTeamModel('sonnet')).toBe(false);
expect(isAnthropicOneMillionContextTeamModel('sonnet[1m]')).toBe(true);
expect(isAnthropicOneMillionContextTeamModel('claude-opus-4-8')).toBe(true);
expect(isAnthropicOneMillionContextTeamModel('claude-opus-4-8[1m]')).toBe(true);
expect(isAnthropicOneMillionContextTeamModel('claude-opus-4-7')).toBe(true);
expect(isAnthropicOneMillionContextTeamModel('claude-opus-4-7[1m]')).toBe(true);
expect(isAnthropicOneMillionContextTeamModel('claude-sonnet-4-6')).toBe(true);
expect(isAnthropicSonnetOneMillionContextTeamModel('sonnet')).toBe(false);
expect(isAnthropicSonnetOneMillionContextTeamModel('sonnet[1m]')).toBe(true);
expect(isAnthropicSonnetOneMillionContextTeamModel('claude-sonnet-4-6')).toBe(true);
expect(isAnthropicSonnetOneMillionContextTeamModel('claude-sonnet-4-6[1m]')).toBe(true);
expect(isAnthropicSonnetOneMillionContextTeamModel('opus[1m]')).toBe(false);
});
});