feat: improve opencode model verification ux

This commit is contained in:
777genius 2026-04-26 10:21:18 +03:00
parent a0f1c92d18
commit 49982a1db8
12 changed files with 5182 additions and 234 deletions

View file

@ -188,7 +188,7 @@ For feature architecture and implementation guidance:
| **Per-task code review** | ✅ Accept / reject / comment | ⚠️ PR-level only | ⚠️ File-level only | ✅ BugBot on PRs | ❌ |
| **Flexible autonomy** | ✅ Granular settings, per-action approval, notifications | ❌ | ⚠️ Plan approval only | ✅ | ✅ |
| **Git worktree isolation** | ✅ Optional | ⚠️ Mandatory | ⚠️ Mandatory | ✅ | ✅ |
| **Multi-agent backend** | ✅ Claude, Codex, more coming soon | ✅ 6+ agents | ✅ 11 providers | ✅ Multi-model | — |
| **Multi-agent backend** | ✅ Codex, Claude, and 75+ providers | ✅ 6+ agents | ✅ 11 providers | ✅ Multi-model | — |
| **Price** | **Free** | Free / $30 user/mo | Free | $0$200/mo | Provider subscription |
---

File diff suppressed because it is too large Load diff

View file

@ -1126,15 +1126,22 @@ function ModelBadges({
? 'bg-emerald-400/15 px-1.5 py-0 text-[10px] text-emerald-200'
: modelRecommendation.level === 'recommended-with-limits'
? 'bg-amber-400/15 px-1.5 py-0 text-[10px] text-amber-200'
: modelRecommendation.level === 'unavailable-in-opencode'
? 'bg-slate-400/15 px-1.5 py-0 text-[10px] text-slate-200'
: 'bg-red-400/15 px-1.5 py-0 text-[10px] text-red-200'
: modelRecommendation.level === 'tested'
? 'bg-sky-400/15 px-1.5 py-0 text-[10px] text-sky-200'
: modelRecommendation.level === 'tested-with-limits'
? 'bg-cyan-400/15 px-1.5 py-0 text-[10px] text-cyan-200'
: modelRecommendation.level === 'unavailable-in-opencode'
? 'bg-slate-400/15 px-1.5 py-0 text-[10px] text-slate-200'
: 'bg-red-400/15 px-1.5 py-0 text-[10px] text-red-200'
}
title={modelRecommendation.reason}
>
{modelRecommendation.level === 'not-recommended' ||
modelRecommendation.level === 'unavailable-in-opencode' ? (
<AlertTriangle className="mr-1 size-3" />
) : modelRecommendation.level === 'tested' ||
modelRecommendation.level === 'tested-with-limits' ? (
<CheckCircle2 className="mr-1 size-3" />
) : (
<Star className="mr-1 size-3 fill-current" />
)}

View file

@ -2,6 +2,7 @@ import React, { useEffect, useMemo, useState } from 'react';
import { ProviderBrandLogo } from '@renderer/components/common/ProviderBrandLogo';
import { Checkbox } from '@renderer/components/ui/checkbox';
import { Input } from '@renderer/components/ui/input';
import { Label } from '@renderer/components/ui/label';
import { Tabs, TabsList, TabsTrigger } from '@renderer/components/ui/tabs';
import {
@ -43,7 +44,7 @@ import { extractProviderScopedBaseModel } from '@renderer/utils/teamModelContext
import { resolveAnthropicLaunchModel } from '@shared/utils/anthropicLaunchModel';
import { getAnthropicDefaultTeamModel } from '@shared/utils/anthropicModelDefaults';
import { isTeamProviderId } from '@shared/utils/teamProvider';
import { AlertTriangle, Info, Star } from 'lucide-react';
import { AlertTriangle, CheckCircle2, Info, Search, Star } from 'lucide-react';
import type { CliProviderStatus, TeamProviderId } from '@shared/types';
@ -169,6 +170,7 @@ export const TeamModelSelector: React.FC<TeamModelSelectorProps> = ({
}) => {
const multimodelEnabled = useStore((s) => s.appConfig?.general?.multimodelEnabled ?? true);
const [recommendedOnly, setRecommendedOnly] = useState(false);
const [modelQuery, setModelQuery] = useState('');
const effectiveProviderId =
disableGeminiOption && isGeminiUiFrozen() && providerId === 'gemini' ? 'anthropic' : providerId;
@ -322,15 +324,41 @@ export const TeamModelSelector: React.FC<TeamModelSelectorProps> = ({
}
}, [effectiveProviderId, hasRecommendedOpenCodeModels]);
useEffect(() => {
setModelQuery('');
}, [effectiveProviderId]);
const visibleModelOptions = useMemo(() => {
const normalizedModelQuery = modelQuery.trim().toLowerCase();
const matchesModelQuery = (option: (typeof modelOptions)[number]): boolean => {
if (!normalizedModelQuery) {
return true;
}
const modelRecommendation =
effectiveProviderId === 'opencode'
? getOpenCodeTeamModelRecommendation(option.value)
: null;
return [
option.value,
option.label,
option.badgeLabel ?? '',
modelRecommendation?.label ?? '',
modelRecommendation?.reason ?? '',
]
.join(' ')
.toLowerCase()
.includes(normalizedModelQuery);
};
if (effectiveProviderId !== 'opencode') {
return modelOptions;
return modelOptions.filter(matchesModelQuery);
}
const concreteOptions = modelOptions
.filter((option) => option.value.trim().length > 0)
.map((option, index) => ({ option, index }))
.filter(({ option }) => !recommendedOnly || isOpenCodeTeamModelRecommended(option.value))
.filter(({ option }) => matchesModelQuery(option))
.sort((left, right) => {
const recommendationOrder = compareOpenCodeTeamModelRecommendations(
left.option.value,
@ -347,8 +375,11 @@ export const TeamModelSelector: React.FC<TeamModelSelectorProps> = ({
return [
...modelOptions.filter((option) => option.value.trim().length === 0),
...concreteOptions,
];
}, [effectiveProviderId, modelOptions, recommendedOnly]);
].filter(matchesModelQuery);
}, [effectiveProviderId, modelOptions, modelQuery, recommendedOnly]);
const concreteModelOptionCount = modelOptions.filter((option) => option.value.trim()).length;
const shouldShowModelSearch = concreteModelOptionCount > 8;
const trimmedModelQuery = modelQuery.trim();
const shouldConstrainModelListHeight = visibleModelOptions.length > 8;
return (
@ -433,6 +464,20 @@ export const TeamModelSelector: React.FC<TeamModelSelectorProps> = ({
list is syncing.
</p>
) : null}
{shouldShowModelSearch ? (
<div className="relative mb-2">
<Search className="pointer-events-none absolute left-3 top-1/2 size-4 -translate-y-1/2 text-[var(--color-text-muted)]" />
<Input
data-testid="team-model-selector-model-search"
value={modelQuery}
onChange={(event) => setModelQuery(event.target.value)}
placeholder="Search models"
aria-label="Search models"
className="h-9 pr-3 text-sm"
style={{ paddingLeft: 40 }}
/>
</div>
) : null}
{hasRecommendedOpenCodeModels ? (
<div className="mb-2 flex w-fit items-center gap-2">
<Checkbox
@ -540,15 +585,22 @@ export const TeamModelSelector: React.FC<TeamModelSelectorProps> = ({
? 'bg-emerald-300/12 border-emerald-300/35 text-emerald-200'
: modelRecommendation.level === 'recommended-with-limits'
? 'bg-amber-300/12 border-amber-300/35 text-amber-200'
: modelRecommendation.level === 'unavailable-in-opencode'
? 'border-slate-300/30 bg-slate-400/10 text-slate-200'
: 'border-red-300/35 bg-red-400/10 text-red-200'
: modelRecommendation.level === 'tested'
? 'bg-sky-300/12 border-sky-300/35 text-sky-200'
: modelRecommendation.level === 'tested-with-limits'
? 'border-cyan-300/30 bg-cyan-400/10 text-cyan-200'
: modelRecommendation.level === 'unavailable-in-opencode'
? 'border-slate-300/30 bg-slate-400/10 text-slate-200'
: 'border-red-300/35 bg-red-400/10 text-red-200'
)}
title={modelRecommendation.reason}
>
{modelRecommendation.level === 'not-recommended' ||
modelRecommendation.level === 'unavailable-in-opencode' ? (
<AlertTriangle className="size-3 shrink-0" />
) : modelRecommendation.level === 'tested' ||
modelRecommendation.level === 'tested-with-limits' ? (
<CheckCircle2 className="size-3 shrink-0" />
) : (
<Star className="size-3 shrink-0 fill-current" />
)}
@ -626,9 +678,13 @@ export const TeamModelSelector: React.FC<TeamModelSelectorProps> = ({
})()
)}
</div>
{effectiveProviderId === 'opencode' && visibleModelOptions.length === 0 ? (
{visibleModelOptions.length === 0 ? (
<div className="rounded-md border border-white/10 px-3 py-2 text-xs text-[var(--color-text-muted)]">
No recommended OpenCode models are available in the current runtime list.
{trimmedModelQuery
? 'No models match this search.'
: effectiveProviderId === 'opencode' && recommendedOnly
? 'No recommended OpenCode models are available in the current runtime list.'
: 'No models are available in the current runtime list.'}
</div>
) : null}
</div>

File diff suppressed because it is too large Load diff

View file

@ -48,6 +48,7 @@ liveDescribe('OpenCode semantic messaging live e2e', () => {
const { bridgeClient, selectedModel, svc, dispose } = await createOpenCodeLiveHarness({
tempDir,
selectedModel: process.env.OPENCODE_E2E_MODEL?.trim() || DEFAULT_MODEL,
projectPath: PROJECT_PATH,
});
const teamName = `opencode-semantic-message-${Date.now()}`;
@ -163,6 +164,7 @@ liveDescribe('OpenCode semantic messaging live e2e', () => {
const { bridgeClient, selectedModel, svc, dispose } = await createOpenCodeLiveHarness({
tempDir,
selectedModel: process.env.OPENCODE_E2E_MODEL?.trim() || DEFAULT_MODEL,
projectPath: PROJECT_PATH,
});
const teamName = `opencode-peer-message-${Date.now()}`;

File diff suppressed because it is too large Load diff

View file

@ -106,6 +106,7 @@ async function runModelScenario(input: {
harness = await createOpenCodeLiveHarness({
tempDir,
selectedModel: input.model,
projectPath,
});
const progressEvents: TeamProvisioningProgress[] = [];

View file

@ -54,6 +54,7 @@ export interface OpenCodeLiveHarness {
export async function createOpenCodeLiveHarness(input: {
tempDir: string;
selectedModel: string;
projectPath?: string;
}): Promise<OpenCodeLiveHarness> {
const orchestratorCli =
process.env.CLAUDE_AGENT_TEAMS_ORCHESTRATOR_CLI_PATH?.trim() || DEFAULT_ORCHESTRATOR_CLI;
@ -104,6 +105,17 @@ export async function createOpenCodeLiveHarness(input: {
svc,
dispose: async () => {
svc.setControlApiBaseUrlResolver(null);
if (input.projectPath?.trim()) {
await readinessBridge
.cleanupOpenCodeHosts({
reason: 'test-harness-dispose',
mode: 'force',
projectPath: input.projectPath,
staleAgeMs: null,
leaseStaleAgeMs: null,
})
.catch(() => undefined);
}
await controlApi.close();
},
};

View file

@ -253,8 +253,10 @@ describe('TeamModelSelector disabled Codex models', () => {
'openrouter/openai/gpt-oss-20b:free',
'openrouter/qwen/qwen3-coder-plus',
'opencode/big-pickle',
'opencode/minimax-m2.5-free',
'openrouter/openai/gpt-oss-120b:free',
'openrouter/qwen/qwen3-coder-flash',
'openrouter/mistralai/codestral-2508',
'openrouter/anthropic/claude-sonnet-4.6',
],
modelVerificationState: 'idle',
modelAvailability: [],
@ -278,11 +280,13 @@ describe('TeamModelSelector disabled Codex models', () => {
await Promise.resolve();
});
expect(host.textContent).toContain('Recommended only');
expect(host.textContent).toContain('qwen/qwen3-coder-flash');
expect(host.textContent).toContain('Recommended');
expect(host.textContent).toContain('anthropic/claude-sonnet-4.6');
expect(host.textContent).toContain('Tested');
expect(host.textContent).toContain('mistralai/codestral-2508');
expect(host.textContent).toContain('Tested');
expect(host.textContent).toContain('minimax-m2.5-free');
expect(host.textContent).toContain('Tested with limits');
expect(host.textContent).toContain('openai/gpt-oss-120b:free');
expect(host.textContent).toContain('Recommended with limits');
expect(host.textContent).toContain('big-pickle');
expect(host.textContent).toContain('qwen/qwen3-coder-plus');
expect(host.textContent).toContain('Unavailable in OpenCode');
@ -292,38 +296,26 @@ describe('TeamModelSelector disabled Codex models', () => {
const buttonTexts = Array.from(host.querySelectorAll('button')).map(
(button) => button.textContent ?? ''
);
const recommendedIndex = buttonTexts.findIndex((text) =>
text.includes('qwen/qwen3-coder-flash')
const sonnetIndex = buttonTexts.findIndex((text) =>
text.includes('anthropic/claude-sonnet-4.6')
);
const testedIndex = buttonTexts.findIndex((text) => text.includes('mistralai/codestral-2508'));
const neutralIndex = buttonTexts.findIndex((text) => text.includes('big-pickle'));
const limitedIndex = buttonTexts.findIndex((text) =>
text.includes('openai/gpt-oss-120b:free')
);
const limitedIndex = buttonTexts.findIndex((text) => text.includes('minimax-m2.5-free'));
const notRecommendedIndex = buttonTexts.findIndex((text) =>
text.includes('openai/gpt-oss-20b:free')
);
const unavailableIndex = buttonTexts.findIndex((text) =>
text.includes('qwen/qwen3-coder-plus')
);
expect(recommendedIndex).toBeGreaterThanOrEqual(0);
expect(limitedIndex).toBeGreaterThan(recommendedIndex);
expect(sonnetIndex).toBeGreaterThanOrEqual(0);
expect(testedIndex).toBeGreaterThanOrEqual(0);
expect(limitedIndex).toBeGreaterThan(testedIndex);
expect(neutralIndex).toBeGreaterThan(limitedIndex);
expect(unavailableIndex).toBeGreaterThan(neutralIndex);
expect(notRecommendedIndex).toBeGreaterThan(unavailableIndex);
await act(async () => {
const checkbox = Array.from(host.querySelectorAll('button')).find(
(button) => button.getAttribute('role') === 'checkbox'
);
checkbox?.click();
await Promise.resolve();
});
expect(host.textContent).toContain('qwen/qwen3-coder-flash');
expect(host.textContent).toContain('openai/gpt-oss-120b:free');
expect(host.textContent).not.toContain('big-pickle');
expect(host.textContent).not.toContain('qwen/qwen3-coder-plus');
expect(host.textContent).not.toContain('openai/gpt-oss-20b:free');
expect(host.textContent).not.toContain('Recommended only');
await act(async () => {
root.unmount();
@ -375,6 +367,23 @@ describe('TeamModelSelector disabled Codex models', () => {
expect(modelGrid).toBeTruthy();
expect(modelGrid?.style.maxHeight).toBe('400px');
expect(modelGrid?.className).toContain('overflow-y-auto');
const searchInput = host.querySelector(
'[data-testid="team-model-selector-model-search"]'
) as HTMLInputElement | null;
expect(searchInput).toBeTruthy();
await act(async () => {
const setValue = Object.getOwnPropertyDescriptor(
window.HTMLInputElement.prototype,
'value'
)?.set;
setValue?.call(searchInput, '5.3');
searchInput?.dispatchEvent(new Event('input', { bubbles: true }));
await Promise.resolve();
});
expect(host.textContent).toContain('5.3 Codex');
expect(host.textContent).not.toContain('5.4 Mini');
await act(async () => {
root.unmount();
@ -413,6 +422,7 @@ describe('TeamModelSelector disabled Codex models', () => {
expect(host.textContent).toContain('5.4');
expect(host.textContent).toContain('5.3 Codex');
expect(host.textContent).not.toContain('Explicit models load from the current runtime');
expect(host.querySelector('[data-testid="team-model-selector-model-search"]')).toBeNull();
await act(async () => {
root.unmount();

View file

@ -586,8 +586,26 @@ describe('RuntimeProviderManagementPanelView', () => {
},
{
providerId: 'openrouter',
modelId: 'openrouter/qwen/qwen3-coder-flash',
displayName: 'qwen/qwen3-coder-flash',
modelId: 'opencode/minimax-m2.5-free',
displayName: 'minimax-m2.5-free',
sourceLabel: 'OpenCode',
free: true,
default: false,
availability: 'untested',
},
{
providerId: 'openrouter',
modelId: 'openrouter/mistralai/codestral-2508',
displayName: 'mistralai/codestral-2508',
sourceLabel: 'OpenRouter',
free: false,
default: false,
availability: 'untested',
},
{
providerId: 'openrouter',
modelId: 'openrouter/anthropic/claude-sonnet-4.6',
displayName: 'anthropic/claude-sonnet-4.6',
sourceLabel: 'OpenRouter',
free: false,
default: false,
@ -621,11 +639,12 @@ describe('RuntimeProviderManagementPanelView', () => {
expect(host.textContent).toContain('openrouter/openai/gpt-oss-20b:free');
expect(host.textContent).toContain('Used for new teams');
expect(host.textContent).toContain('Model probe passed');
expect(host.textContent).toContain('Recommended');
expect(host.textContent).toContain('Not recommended');
expect(host.textContent).toContain('Unavailable in OpenCode');
expect(host.textContent).toContain('Recommended only');
expect(host.textContent).toContain('Recommended');
expect(host.textContent).toContain('Recommended with limits');
expect(host.textContent).toContain('Tested');
expect(host.textContent).toContain('Tested with limits');
expect(host.textContent).not.toContain('Recommended only');
expect(host.textContent).not.toContain('Set OpenCode default');
expect(
Array.from(host.querySelectorAll('button')).some(
@ -640,11 +659,8 @@ describe('RuntimeProviderManagementPanelView', () => {
) as HTMLElement | undefined;
expect(connectedBadge?.style.color).toBeTruthy();
expect(
(
host.querySelector(
'[data-testid="runtime-provider-model-search"]'
) as HTMLElement | null
)?.style.paddingLeft
(host.querySelector('[data-testid="runtime-provider-model-search"]') as HTMLElement | null)
?.style.paddingLeft
).toBe('42px');
expect(
(host.querySelector('[data-testid="runtime-provider-model-list"]') as HTMLElement | null)
@ -659,10 +675,13 @@ describe('RuntimeProviderManagementPanelView', () => {
'[data-testid="runtime-provider-model-result-openrouter/openai/gpt-oss-20b:free"]'
) as HTMLElement | null;
expect(modelResult?.style.color).toBe('#86efac');
expect((host.textContent ?? '').indexOf('qwen/qwen3-coder-flash')).toBeLessThan(
(host.textContent ?? '').indexOf('openai/gpt-oss-120b:free')
expect((host.textContent ?? '').indexOf('mistralai/codestral-2508')).toBeLessThan(
(host.textContent ?? '').indexOf('anthropic/claude-sonnet-4.6')
);
expect((host.textContent ?? '').indexOf('openai/gpt-oss-120b:free')).toBeLessThan(
expect((host.textContent ?? '').indexOf('anthropic/claude-sonnet-4.6')).toBeLessThan(
(host.textContent ?? '').indexOf('minimax-m2.5-free')
);
expect((host.textContent ?? '').indexOf('minimax-m2.5-free')).toBeLessThan(
(host.textContent ?? '').indexOf('opencode/big-pickle')
);
expect((host.textContent ?? '').indexOf('opencode/big-pickle')).toBeLessThan(
@ -671,29 +690,6 @@ describe('RuntimeProviderManagementPanelView', () => {
expect((host.textContent ?? '').indexOf('qwen/qwen3-coder-plus')).toBeLessThan(
(host.textContent ?? '').indexOf('openrouter/openai/gpt-oss-20b:free')
);
await act(async () => {
const checkbox = Array.from(host.querySelectorAll('button')).find(
(button) => button.getAttribute('role') === 'checkbox'
);
checkbox?.click();
await Promise.resolve();
});
expect(host.textContent).toContain('qwen/qwen3-coder-flash');
expect(host.textContent).toContain('openai/gpt-oss-120b:free');
expect(host.textContent).not.toContain('opencode/big-pickle');
expect(host.textContent).not.toContain('qwen/qwen3-coder-plus');
expect(host.textContent).not.toContain('openrouter/openai/gpt-oss-20b:free');
await act(async () => {
const checkbox = Array.from(host.querySelectorAll('button')).find(
(button) => button.getAttribute('role') === 'checkbox'
);
checkbox?.click();
await Promise.resolve();
});
await act(async () => {
host
.querySelector(
@ -854,14 +850,10 @@ describe('RuntimeProviderManagementPanelView', () => {
expect(logo).not.toBeNull();
expect(logo?.className).toContain('runtime-provider-brand-icon');
expect(logo?.querySelector('svg,img')).not.toBeNull();
expect(logo?.getAttribute('style')).toContain(
'--runtime-provider-brand-fallback-background'
);
expect(logo?.getAttribute('style')).toContain('--runtime-provider-brand-fallback-background');
expect(logo?.getAttribute('style')).toContain('--runtime-provider-brand-fallback-border');
if (logo?.querySelector('svg')) {
expect(logo.getAttribute('style')).toContain(
'--runtime-provider-brand-fallback-color'
);
expect(logo.getAttribute('style')).toContain('--runtime-provider-brand-fallback-color');
}
}
});

View file

@ -7,104 +7,94 @@ import {
} from '@renderer/utils/openCodeModelRecommendations';
describe('getOpenCodeTeamModelRecommendation', () => {
it('marks models that passed real OpenCode Agent Teams E2E as recommended', () => {
expect(getOpenCodeTeamModelRecommendation('openrouter/qwen/qwen3-coder-flash')).toMatchObject({
level: 'recommended',
label: 'Recommended',
it('keeps Claude Sonnet 4.6 as tested while recommendations are disabled', () => {
expect(
getOpenCodeTeamModelRecommendation('openrouter/anthropic/claude-sonnet-4.6')
).toMatchObject({
level: 'tested',
label: 'Tested',
});
expect(isOpenCodeTeamModelRecommended('openrouter/anthropic/claude-sonnet-4.6')).toBe(false);
});
it('marks models that passed real OpenCode Agent Teams smoke E2E as tested', () => {
expect(getOpenCodeTeamModelRecommendation('openrouter/mistralai/codestral-2508')).toMatchObject({
level: 'tested',
label: 'Tested',
});
expect(
getOpenCodeTeamModelRecommendation(' OPENROUTER/GOOGLE/GEMINI-3-FLASH-PREVIEW ')
).toMatchObject({
level: 'recommended',
label: 'Recommended',
level: 'tested',
label: 'Tested',
});
expect(getOpenCodeTeamModelRecommendation('openrouter/moonshotai/kimi-k2.6')).toMatchObject({
level: 'recommended',
label: 'Recommended',
level: 'tested',
label: 'Tested',
});
expect(getOpenCodeTeamModelRecommendation('openrouter/z-ai/glm-5.1')).toMatchObject({
level: 'recommended',
label: 'Recommended',
});
expect(getOpenCodeTeamModelRecommendation('openrouter/z-ai/glm-5')).toMatchObject({
level: 'recommended',
label: 'Recommended',
});
expect(getOpenCodeTeamModelRecommendation('openrouter/minimax/minimax-m2.7')).toMatchObject({
level: 'recommended',
label: 'Recommended',
});
expect(
getOpenCodeTeamModelRecommendation('openrouter/google/gemini-3.1-pro-preview')
).toMatchObject({
level: 'recommended',
label: 'Recommended',
});
expect(
getOpenCodeTeamModelRecommendation('openrouter/anthropic/claude-sonnet-4.6')
).toMatchObject({
level: 'recommended',
label: 'Recommended',
});
expect(getOpenCodeTeamModelRecommendation('openrouter/anthropic/claude-opus-4.6')).toMatchObject({
level: 'recommended',
label: 'Recommended',
});
expect(getOpenCodeTeamModelRecommendation('openrouter/anthropic/claude-opus-4.7')).toMatchObject({
level: 'recommended',
label: 'Recommended',
});
expect(getOpenCodeTeamModelRecommendation('openrouter/mistralai/devstral-2512')).toMatchObject({
level: 'recommended',
label: 'Recommended',
});
expect(getOpenCodeTeamModelRecommendation('openrouter/openai/gpt-5.4')).toMatchObject({
level: 'recommended',
label: 'Recommended',
level: 'tested',
label: 'Tested',
});
expect(getOpenCodeTeamModelRecommendation('openrouter/openai/gpt-5.3-codex')).toMatchObject({
level: 'recommended',
label: 'Recommended',
level: 'tested',
label: 'Tested',
});
expect(getOpenCodeTeamModelRecommendation('openrouter/x-ai/grok-4-fast')).toMatchObject({
level: 'recommended',
label: 'Recommended',
expect(getOpenCodeTeamModelRecommendation('openrouter/openai/gpt-5-nano')).toMatchObject({
level: 'tested',
label: 'Tested',
});
expect(getOpenCodeTeamModelRecommendation('openrouter/x-ai/grok-4.1-fast')).toMatchObject({
level: 'recommended',
label: 'Recommended',
});
expect(getOpenCodeTeamModelRecommendation('openrouter/xiaomi/mimo-v2-pro')).toMatchObject({
level: 'recommended',
label: 'Recommended',
});
expect(getOpenCodeTeamModelRecommendation('openrouter/openai/gpt-5.1-codex')).toMatchObject({
level: 'recommended',
label: 'Recommended',
});
expect(getOpenCodeTeamModelRecommendation('openrouter/qwen/qwen3-max')).toMatchObject({
level: 'recommended',
label: 'Recommended',
expect(getOpenCodeTeamModelRecommendation('openrouter/minimax/minimax-m2')).toMatchObject({
level: 'tested',
label: 'Tested',
});
expect(
getOpenCodeTeamModelRecommendation('openrouter/mistralai/mistral-medium-3.1')
getOpenCodeTeamModelRecommendation('openrouter/google/gemma-4-26b-a4b-it')
).toMatchObject({
level: 'recommended',
label: 'Recommended',
level: 'tested',
label: 'Tested',
});
expect(getOpenCodeTeamModelRecommendation('openrouter/x-ai/grok-4.1-fast')).toMatchObject({
level: 'tested',
label: 'Tested',
});
expect(
getOpenCodeTeamModelRecommendation('openrouter/google/gemini-3.1-flash-lite-preview')
).toMatchObject({
level: 'recommended',
label: 'Recommended',
level: 'tested',
label: 'Tested',
});
expect(isOpenCodeTeamModelRecommended('openrouter/qwen/qwen3-coder-flash')).toBe(true);
expect(getOpenCodeTeamModelRecommendation('openrouter/z-ai/glm-4.7-flash')).toMatchObject({
level: 'tested',
label: 'Tested',
});
expect(getOpenCodeTeamModelRecommendation('openrouter/z-ai/glm-4.7')).toMatchObject({
level: 'tested',
label: 'Tested',
});
expect(getOpenCodeTeamModelRecommendation('openrouter/stepfun/step-3.5-flash')).toMatchObject({
level: 'tested',
label: 'Tested',
});
expect(getOpenCodeTeamModelRecommendation('openrouter/minimax/minimax-m2.1')).toMatchObject({
level: 'tested',
label: 'Tested',
});
expect(getOpenCodeTeamModelRecommendation('openrouter/minimax/minimax-m2.7')).toMatchObject({
level: 'tested',
label: 'Tested',
});
expect(getOpenCodeTeamModelRecommendation('openrouter/xiaomi/mimo-v2-pro')).toMatchObject({
level: 'tested',
label: 'Tested',
});
expect(isOpenCodeTeamModelRecommended('openrouter/mistralai/codestral-2508')).toBe(false);
});
it('keeps similarly named models distinct when real E2E disagreed', () => {
expect(getOpenCodeTeamModelRecommendation('opencode/minimax-m2.5-free')).toMatchObject({
level: 'recommended-with-limits',
label: 'Recommended with limits',
level: 'tested-with-limits',
label: 'Tested with limits',
});
expect(
getOpenCodeTeamModelRecommendation('openrouter/minimax/minimax-m2.5:free')
@ -113,53 +103,50 @@ describe('getOpenCodeTeamModelRecommendation', () => {
});
});
it('marks passing free routes as recommended with limits', () => {
expect(getOpenCodeTeamModelRecommendation('openrouter/openai/gpt-oss-120b:free')).toMatchObject(
{
level: 'recommended-with-limits',
label: 'Recommended with limits',
}
);
expect(isOpenCodeTeamModelRecommended('openrouter/openai/gpt-oss-120b:free')).toBe(true);
it('marks models with real launch or messaging failures as not recommended', () => {
for (const modelId of [
'openrouter/openai/gpt-oss-20b:free',
'openrouter/openai/gpt-oss-120b:free',
'openrouter/google/gemini-3-pro-preview',
'openrouter/google/gemini-2.5-flash-lite',
'openrouter/deepseek/deepseek-v3.2',
'openrouter/x-ai/grok-code-fast-1',
'openrouter/openai/gpt-5.1',
'openrouter/openai/gpt-5.4',
'openrouter/z-ai/glm-5-turbo',
'openrouter/qwen/qwen3.6-plus',
'openrouter/qwen/qwen3-coder-flash',
'openrouter/qwen/qwen3-coder',
'openrouter/google/gemini-3.1-pro-preview',
'openrouter/anthropic/claude-opus-4.6',
'openrouter/mistralai/mistral-medium-3',
'openrouter/nvidia/nemotron-nano-9b-v2',
'openrouter/liquid/lfm-2.5-1.2b-thinking:free',
'openrouter/deepseek/deepseek-r1-distill-llama-70b',
]) {
expect(getOpenCodeTeamModelRecommendation(modelId)).toMatchObject({
level: 'not-recommended',
label: 'Not recommended',
});
}
});
it('marks models with real launch or messaging failures as not recommended', () => {
expect(getOpenCodeTeamModelRecommendation('openrouter/openai/gpt-oss-20b:free')).toMatchObject({
level: 'not-recommended',
label: 'Not recommended',
});
expect(
getOpenCodeTeamModelRecommendation('openrouter/google/gemini-3-pro-preview')
).toMatchObject({
level: 'not-recommended',
label: 'Not recommended',
});
expect(
getOpenCodeTeamModelRecommendation('openrouter/google/gemini-2.5-flash-lite')
).toMatchObject({
level: 'not-recommended',
label: 'Not recommended',
});
expect(getOpenCodeTeamModelRecommendation('openrouter/deepseek/deepseek-v3.2')).toMatchObject({
level: 'not-recommended',
label: 'Not recommended',
});
expect(getOpenCodeTeamModelRecommendation('openrouter/x-ai/grok-code-fast-1')).toMatchObject({
level: 'not-recommended',
label: 'Not recommended',
});
expect(getOpenCodeTeamModelRecommendation('openrouter/openai/gpt-5.2-codex')).toMatchObject({
level: 'not-recommended',
label: 'Not recommended',
});
expect(getOpenCodeTeamModelRecommendation('openrouter/moonshotai/kimi-k2-thinking')).toMatchObject({
level: 'not-recommended',
label: 'Not recommended',
});
expect(getOpenCodeTeamModelRecommendation('openrouter/openai/gpt-5.1-chat')).toMatchObject({
level: 'not-recommended',
label: 'Not recommended',
});
it('does not mark provider allowance or depleted-credit failures as model verdicts', () => {
for (const modelId of [
'openrouter/openai/gpt-5.1-codex-max',
'openrouter/openai/gpt-5.2-chat',
'openrouter/x-ai/grok-3',
'openrouter/anthropic/claude-sonnet-4',
'openrouter/anthropic/claude-opus-4.1',
'openrouter/openai/gpt-5-pro',
'openrouter/openai/gpt-5.2-pro',
'openrouter/openai/gpt-5.4-pro',
'openrouter/moonshotai/kimi-k2.5',
'openrouter/openai/gpt-5-mini',
'openrouter/google/gemini-2.5-flash-lite-preview-09-2025',
]) {
expect(getOpenCodeTeamModelRecommendation(modelId)).toBeNull();
}
});
it('marks OpenRouter routes missing from the OpenCode catalog as unavailable, not bad', () => {
@ -171,6 +158,124 @@ describe('getOpenCodeTeamModelRecommendation', () => {
level: 'unavailable-in-opencode',
label: 'Unavailable in OpenCode',
});
expect(getOpenCodeTeamModelRecommendation('openrouter/qwen/qwen3-coder:free')).toMatchObject({
level: 'unavailable-in-opencode',
label: 'Unavailable in OpenCode',
});
expect(
getOpenCodeTeamModelRecommendation('openrouter/qwen/qwen3-next-80b-a3b-instruct:free')
).toMatchObject({
level: 'unavailable-in-opencode',
label: 'Unavailable in OpenCode',
});
expect(
getOpenCodeTeamModelRecommendation('openrouter/google/gemini-2.0-flash-lite-001')
).toMatchObject({
level: 'unavailable-in-opencode',
label: 'Unavailable in OpenCode',
});
expect(getOpenCodeTeamModelRecommendation('openrouter/openai/gpt-4.1-nano')).toMatchObject({
level: 'unavailable-in-opencode',
label: 'Unavailable in OpenCode',
});
expect(
getOpenCodeTeamModelRecommendation('openrouter/openai/gpt-4o-mini-2024-07-18')
).toMatchObject({
level: 'unavailable-in-opencode',
label: 'Unavailable in OpenCode',
});
expect(
getOpenCodeTeamModelRecommendation('openrouter/openai/gpt-4o-mini-search-preview')
).toMatchObject({
level: 'unavailable-in-opencode',
label: 'Unavailable in OpenCode',
});
expect(getOpenCodeTeamModelRecommendation('openrouter/qwen/qwen-plus')).toMatchObject({
level: 'unavailable-in-opencode',
label: 'Unavailable in OpenCode',
});
expect(getOpenCodeTeamModelRecommendation('openrouter/qwen/qwen-turbo')).toMatchObject({
level: 'unavailable-in-opencode',
label: 'Unavailable in OpenCode',
});
expect(
getOpenCodeTeamModelRecommendation('openrouter/qwen/qwen3-235b-a22b-2507')
).toMatchObject({
level: 'unavailable-in-opencode',
label: 'Unavailable in OpenCode',
});
expect(
getOpenCodeTeamModelRecommendation('openrouter/deepseek/deepseek-v3.2-exp')
).toMatchObject({
level: 'unavailable-in-opencode',
label: 'Unavailable in OpenCode',
});
expect(getOpenCodeTeamModelRecommendation('openrouter/qwen/qwen3-32b')).toMatchObject({
level: 'unavailable-in-opencode',
label: 'Unavailable in OpenCode',
});
expect(getOpenCodeTeamModelRecommendation('openrouter/qwen/qwen3-14b')).toMatchObject({
level: 'unavailable-in-opencode',
label: 'Unavailable in OpenCode',
});
expect(getOpenCodeTeamModelRecommendation('openrouter/qwen/qwen3-8b')).toMatchObject({
level: 'unavailable-in-opencode',
label: 'Unavailable in OpenCode',
});
expect(getOpenCodeTeamModelRecommendation('openrouter/qwen/qwq-32b')).toMatchObject({
level: 'unavailable-in-opencode',
label: 'Unavailable in OpenCode',
});
expect(getOpenCodeTeamModelRecommendation('openrouter/deepseek/deepseek-chat')).toMatchObject({
level: 'unavailable-in-opencode',
label: 'Unavailable in OpenCode',
});
expect(getOpenCodeTeamModelRecommendation('openrouter/mistralai/mistral-nemo')).toMatchObject({
level: 'unavailable-in-opencode',
label: 'Unavailable in OpenCode',
});
expect(
getOpenCodeTeamModelRecommendation('openrouter/mistralai/mistral-small-24b-instruct-2501')
).toMatchObject({
level: 'unavailable-in-opencode',
label: 'Unavailable in OpenCode',
});
expect(
getOpenCodeTeamModelRecommendation('openrouter/cohere/command-r7b-12-2024')
).toMatchObject({
level: 'unavailable-in-opencode',
label: 'Unavailable in OpenCode',
});
expect(getOpenCodeTeamModelRecommendation('openrouter/cohere/command-r-08-2024')).toMatchObject(
{
level: 'unavailable-in-opencode',
label: 'Unavailable in OpenCode',
}
);
expect(getOpenCodeTeamModelRecommendation('openrouter/rekaai/reka-flash-3')).toMatchObject({
level: 'unavailable-in-opencode',
label: 'Unavailable in OpenCode',
});
expect(getOpenCodeTeamModelRecommendation('openrouter/rekaai/reka-edge')).toMatchObject({
level: 'unavailable-in-opencode',
label: 'Unavailable in OpenCode',
});
expect(
getOpenCodeTeamModelRecommendation('openrouter/nvidia/nemotron-3-nano-30b-a3b')
).toMatchObject({
level: 'unavailable-in-opencode',
label: 'Unavailable in OpenCode',
});
expect(getOpenCodeTeamModelRecommendation('openrouter/minimax/minimax-01')).toMatchObject({
level: 'unavailable-in-opencode',
label: 'Unavailable in OpenCode',
});
expect(
getOpenCodeTeamModelRecommendation('openrouter/nvidia/llama-3.3-nemotron-super-49b-v1.5')
).toMatchObject({
level: 'unavailable-in-opencode',
label: 'Unavailable in OpenCode',
});
expect(getOpenCodeTeamModelRecommendation('openrouter/qwen/qwen3-max-thinking')).toMatchObject({
level: 'unavailable-in-opencode',
label: 'Unavailable in OpenCode',
@ -181,35 +286,528 @@ describe('getOpenCodeTeamModelRecommendation', () => {
level: 'unavailable-in-opencode',
label: 'Unavailable in OpenCode',
});
expect(getOpenCodeTeamModelRecommendation('openrouter/mistralai/devstral-medium')).toMatchObject(
expect(
getOpenCodeTeamModelRecommendation('openrouter/mistralai/devstral-medium')
).toMatchObject({
level: 'unavailable-in-opencode',
label: 'Unavailable in OpenCode',
});
expect(getOpenCodeTeamModelRecommendation('openrouter/mistralai/devstral-small')).toMatchObject(
{
level: 'unavailable-in-opencode',
label: 'Unavailable in OpenCode',
}
);
expect(
getOpenCodeTeamModelRecommendation('openrouter/mistralai/ministral-14b-2512')
).toMatchObject({
level: 'unavailable-in-opencode',
label: 'Unavailable in OpenCode',
});
expect(
getOpenCodeTeamModelRecommendation('openrouter/mistralai/ministral-8b-2512')
).toMatchObject({
level: 'unavailable-in-opencode',
label: 'Unavailable in OpenCode',
});
expect(
getOpenCodeTeamModelRecommendation('openrouter/mistralai/ministral-3b-2512')
).toMatchObject({
level: 'unavailable-in-opencode',
label: 'Unavailable in OpenCode',
});
expect(getOpenCodeTeamModelRecommendation('openrouter/minimax/minimax-m2-her')).toMatchObject({
level: 'unavailable-in-opencode',
label: 'Unavailable in OpenCode',
});
expect(getOpenCodeTeamModelRecommendation('openrouter/xiaomi/mimo-v2.5')).toMatchObject({
level: 'unavailable-in-opencode',
label: 'Unavailable in OpenCode',
});
expect(getOpenCodeTeamModelRecommendation('openrouter/xiaomi/mimo-v2.5-pro')).toMatchObject({
level: 'unavailable-in-opencode',
label: 'Unavailable in OpenCode',
});
expect(getOpenCodeTeamModelRecommendation('openrouter/x-ai/grok-4.20')).toMatchObject({
level: 'unavailable-in-opencode',
label: 'Unavailable in OpenCode',
});
expect(
getOpenCodeTeamModelRecommendation('openrouter/google/gemini-3.1-flash-image-preview')
).toMatchObject({
level: 'unavailable-in-opencode',
label: 'Unavailable in OpenCode',
});
expect(getOpenCodeTeamModelRecommendation('openrouter/z-ai/glm-5v-turbo')).toMatchObject({
level: 'unavailable-in-opencode',
label: 'Unavailable in OpenCode',
});
expect(
getOpenCodeTeamModelRecommendation('openrouter/x-ai/grok-4.20-multi-agent')
).toMatchObject({
level: 'unavailable-in-opencode',
label: 'Unavailable in OpenCode',
});
expect(
getOpenCodeTeamModelRecommendation('openrouter/mistralai/mistral-small-creative')
).toMatchObject({
level: 'unavailable-in-opencode',
label: 'Unavailable in OpenCode',
});
expect(getOpenCodeTeamModelRecommendation('openrouter/openai/gpt-5.3-chat')).toMatchObject({
level: 'unavailable-in-opencode',
label: 'Unavailable in OpenCode',
});
expect(
getOpenCodeTeamModelRecommendation('openrouter/mistralai/voxtral-small-24b-2507')
).toMatchObject({
level: 'unavailable-in-opencode',
label: 'Unavailable in OpenCode',
});
expect(getOpenCodeTeamModelRecommendation('openrouter/openai/gpt-5-chat')).toMatchObject({
level: 'unavailable-in-opencode',
label: 'Unavailable in OpenCode',
});
expect(
getOpenCodeTeamModelRecommendation('openrouter/qwen/qwen-2.5-72b-instruct')
).toMatchObject({
level: 'unavailable-in-opencode',
label: 'Unavailable in OpenCode',
});
expect(
getOpenCodeTeamModelRecommendation('openrouter/tngtech/deepseek-r1t2-chimera')
).toMatchObject({
level: 'unavailable-in-opencode',
label: 'Unavailable in OpenCode',
});
expect(
getOpenCodeTeamModelRecommendation('openrouter/google/gemini-2.5-pro-preview')
).toMatchObject({
level: 'unavailable-in-opencode',
label: 'Unavailable in OpenCode',
});
expect(getOpenCodeTeamModelRecommendation('openrouter/mistralai/mistral-saba')).toMatchObject({
level: 'unavailable-in-opencode',
label: 'Unavailable in OpenCode',
});
expect(
getOpenCodeTeamModelRecommendation('openrouter/mistralai/mistral-large-2411')
).toMatchObject({
level: 'unavailable-in-opencode',
label: 'Unavailable in OpenCode',
});
expect(
getOpenCodeTeamModelRecommendation('openrouter/qwen/qwen3-vl-30b-a3b-instruct')
).toMatchObject({
level: 'unavailable-in-opencode',
label: 'Unavailable in OpenCode',
});
expect(
getOpenCodeTeamModelRecommendation('openrouter/inclusionai/ling-2.6-1t:free')
).toMatchObject({
level: 'unavailable-in-opencode',
label: 'Unavailable in OpenCode',
});
expect(
getOpenCodeTeamModelRecommendation('openrouter/inclusionai/ling-2.6-flash:free')
).toMatchObject({
level: 'unavailable-in-opencode',
label: 'Unavailable in OpenCode',
});
expect(
getOpenCodeTeamModelRecommendation('openrouter/meta-llama/llama-3.1-8b-instruct')
).toMatchObject({
level: 'unavailable-in-opencode',
label: 'Unavailable in OpenCode',
});
expect(
getOpenCodeTeamModelRecommendation('openrouter/qwen/qwen-2.5-7b-instruct')
).toMatchObject({
level: 'unavailable-in-opencode',
label: 'Unavailable in OpenCode',
});
expect(getOpenCodeTeamModelRecommendation('openrouter/amazon/nova-lite-v1')).toMatchObject({
level: 'unavailable-in-opencode',
label: 'Unavailable in OpenCode',
});
expect(getOpenCodeTeamModelRecommendation('openrouter/z-ai/glm-4-32b')).toMatchObject({
level: 'unavailable-in-opencode',
label: 'Unavailable in OpenCode',
});
expect(
getOpenCodeTeamModelRecommendation('openrouter/bytedance-seed/seed-1.6-flash')
).toMatchObject({
level: 'unavailable-in-opencode',
label: 'Unavailable in OpenCode',
});
expect(getOpenCodeTeamModelRecommendation('openrouter/meta-llama/llama-4-scout')).toMatchObject(
{
level: 'unavailable-in-opencode',
label: 'Unavailable in OpenCode',
}
);
expect(
getOpenCodeTeamModelRecommendation('openrouter/meta-llama/llama-3.3-70b-instruct')
).toMatchObject({
level: 'unavailable-in-opencode',
label: 'Unavailable in OpenCode',
});
expect(
getOpenCodeTeamModelRecommendation('openrouter/bytedance-seed/seed-2.0-mini')
).toMatchObject({
level: 'unavailable-in-opencode',
label: 'Unavailable in OpenCode',
});
expect(
getOpenCodeTeamModelRecommendation('openrouter/qwen/qwen3-vl-32b-instruct')
).toMatchObject({
level: 'unavailable-in-opencode',
label: 'Unavailable in OpenCode',
});
expect(
getOpenCodeTeamModelRecommendation('openrouter/alibaba/tongyi-deepresearch-30b-a3b')
).toMatchObject({
level: 'unavailable-in-opencode',
label: 'Unavailable in OpenCode',
});
expect(
getOpenCodeTeamModelRecommendation('openrouter/arcee-ai/trinity-large-preview')
).toMatchObject({
level: 'unavailable-in-opencode',
label: 'Unavailable in OpenCode',
});
expect(getOpenCodeTeamModelRecommendation('openrouter/amazon/nova-micro-v1')).toMatchObject({
level: 'unavailable-in-opencode',
label: 'Unavailable in OpenCode',
});
expect(getOpenCodeTeamModelRecommendation('openrouter/arcee-ai/trinity-mini')).toMatchObject({
level: 'unavailable-in-opencode',
label: 'Unavailable in OpenCode',
});
expect(getOpenCodeTeamModelRecommendation('openrouter/qwen/qwen3.5-9b')).toMatchObject({
level: 'unavailable-in-opencode',
label: 'Unavailable in OpenCode',
});
expect(
getOpenCodeTeamModelRecommendation('openrouter/essentialai/rnj-1-instruct')
).toMatchObject({
level: 'unavailable-in-opencode',
label: 'Unavailable in OpenCode',
});
expect(getOpenCodeTeamModelRecommendation('openrouter/upstage/solar-pro-3')).toMatchObject({
level: 'unavailable-in-opencode',
label: 'Unavailable in OpenCode',
});
expect(
getOpenCodeTeamModelRecommendation('openrouter/allenai/olmo-3.1-32b-instruct')
).toMatchObject({
level: 'unavailable-in-opencode',
label: 'Unavailable in OpenCode',
});
expect(
getOpenCodeTeamModelRecommendation('openrouter/qwen/qwen-plus-2025-07-28')
).toMatchObject({
level: 'unavailable-in-opencode',
label: 'Unavailable in OpenCode',
});
expect(getOpenCodeTeamModelRecommendation('openrouter/tencent/hy3-preview:free')).toMatchObject(
{
level: 'unavailable-in-opencode',
label: 'Unavailable in OpenCode',
}
);
expect(
getOpenCodeTeamModelRecommendation('openrouter/qwen/qwen3-vl-8b-instruct')
).toMatchObject({
level: 'unavailable-in-opencode',
label: 'Unavailable in OpenCode',
});
expect(
getOpenCodeTeamModelRecommendation('openrouter/nex-agi/deepseek-v3.1-nex-n1')
).toMatchObject({
level: 'unavailable-in-opencode',
label: 'Unavailable in OpenCode',
});
expect(
getOpenCodeTeamModelRecommendation('openrouter/baidu/ernie-4.5-vl-28b-a3b')
).toMatchObject({
level: 'unavailable-in-opencode',
label: 'Unavailable in OpenCode',
});
expect(getOpenCodeTeamModelRecommendation('openrouter/thedrummer/rocinante-12b')).toMatchObject(
{
level: 'unavailable-in-opencode',
label: 'Unavailable in OpenCode',
}
);
expect(
getOpenCodeTeamModelRecommendation('openrouter/meta-llama/llama-3.1-70b-instruct')
).toMatchObject({
level: 'unavailable-in-opencode',
label: 'Unavailable in OpenCode',
});
expect(
getOpenCodeTeamModelRecommendation('openrouter/qwen/qwen-plus-2025-07-28:thinking')
).toMatchObject({
level: 'unavailable-in-opencode',
label: 'Unavailable in OpenCode',
});
expect(getOpenCodeTeamModelRecommendation('openrouter/z-ai/glm-4.6v')).toMatchObject({
level: 'unavailable-in-opencode',
label: 'Unavailable in OpenCode',
});
expect(getOpenCodeTeamModelRecommendation('openrouter/anthropic/claude-3-haiku')).toMatchObject(
{
level: 'unavailable-in-opencode',
label: 'Unavailable in OpenCode',
}
);
expect(
getOpenCodeTeamModelRecommendation('openrouter/bytedance-seed/seed-2.0-lite')
).toMatchObject({
level: 'unavailable-in-opencode',
label: 'Unavailable in OpenCode',
});
expect(getOpenCodeTeamModelRecommendation('openrouter/qwen/qwen3-235b-a22b')).toMatchObject({
level: 'unavailable-in-opencode',
label: 'Unavailable in OpenCode',
});
expect(getOpenCodeTeamModelRecommendation('openrouter/qwen/qwen3.5-122b-a10b')).toMatchObject({
level: 'unavailable-in-opencode',
label: 'Unavailable in OpenCode',
});
expect(
getOpenCodeTeamModelRecommendation('openrouter/deepseek/deepseek-r1-0528')
).toMatchObject({
level: 'unavailable-in-opencode',
label: 'Unavailable in OpenCode',
});
expect(getOpenCodeTeamModelRecommendation('openrouter/amazon/nova-2-lite-v1')).toMatchObject({
level: 'unavailable-in-opencode',
label: 'Unavailable in OpenCode',
});
expect(getOpenCodeTeamModelRecommendation('openrouter/openai/o3-mini')).toMatchObject({
level: 'unavailable-in-opencode',
label: 'Unavailable in OpenCode',
});
expect(
getOpenCodeTeamModelRecommendation('openrouter/mistralai/mistral-large-2407')
).toMatchObject({
level: 'unavailable-in-opencode',
label: 'Unavailable in OpenCode',
});
expect(
getOpenCodeTeamModelRecommendation('openrouter/thedrummer/unslopnemo-12b')
).toMatchObject({
level: 'unavailable-in-opencode',
label: 'Unavailable in OpenCode',
});
expect(
getOpenCodeTeamModelRecommendation('openrouter/qwen/qwen3-vl-235b-a22b-instruct')
).toMatchObject({
level: 'unavailable-in-opencode',
label: 'Unavailable in OpenCode',
});
expect(
getOpenCodeTeamModelRecommendation('openrouter/qwen/qwen3-vl-8b-thinking')
).toMatchObject({
level: 'unavailable-in-opencode',
label: 'Unavailable in OpenCode',
});
expect(
getOpenCodeTeamModelRecommendation('openrouter/kwaipilot/kat-coder-pro-v2')
).toMatchObject({
level: 'unavailable-in-opencode',
label: 'Unavailable in OpenCode',
});
expect(getOpenCodeTeamModelRecommendation('openrouter/openai/o4-mini-high')).toMatchObject({
level: 'unavailable-in-opencode',
label: 'Unavailable in OpenCode',
});
expect(getOpenCodeTeamModelRecommendation('openrouter/openai/o3-mini-high')).toMatchObject({
level: 'unavailable-in-opencode',
label: 'Unavailable in OpenCode',
});
expect(getOpenCodeTeamModelRecommendation('openrouter/openai/gpt-4o')).toMatchObject({
level: 'unavailable-in-opencode',
label: 'Unavailable in OpenCode',
});
expect(
getOpenCodeTeamModelRecommendation('openrouter/cohere/command-r-plus-08-2024')
).toMatchObject({
level: 'unavailable-in-opencode',
label: 'Unavailable in OpenCode',
});
expect(
getOpenCodeTeamModelRecommendation('openrouter/qwen/qwen3-vl-30b-a3b-thinking')
).toMatchObject({
level: 'unavailable-in-opencode',
label: 'Unavailable in OpenCode',
});
expect(getOpenCodeTeamModelRecommendation('openrouter/sao10k/l3.1-euryale-70b')).toMatchObject({
level: 'unavailable-in-opencode',
label: 'Unavailable in OpenCode',
});
expect(getOpenCodeTeamModelRecommendation('openrouter/qwen/qwen3.5-27b')).toMatchObject({
level: 'unavailable-in-opencode',
label: 'Unavailable in OpenCode',
});
expect(getOpenCodeTeamModelRecommendation('openrouter/arcee-ai/virtuoso-large')).toMatchObject({
level: 'unavailable-in-opencode',
label: 'Unavailable in OpenCode',
});
expect(getOpenCodeTeamModelRecommendation('openrouter/openai/gpt-3.5-turbo')).toMatchObject({
level: 'unavailable-in-opencode',
label: 'Unavailable in OpenCode',
});
expect(getOpenCodeTeamModelRecommendation('openrouter/bytedance-seed/seed-1.6')).toMatchObject({
level: 'unavailable-in-opencode',
label: 'Unavailable in OpenCode',
});
expect(
getOpenCodeTeamModelRecommendation('openrouter/nvidia/llama-3.1-nemotron-70b-instruct')
).toMatchObject({
level: 'unavailable-in-opencode',
label: 'Unavailable in OpenCode',
});
expect(getOpenCodeTeamModelRecommendation('openrouter/qwen/qwen-vl-max')).toMatchObject({
level: 'unavailable-in-opencode',
label: 'Unavailable in OpenCode',
});
expect(
getOpenCodeTeamModelRecommendation('openrouter/qwen/qwen3-vl-235b-a22b-thinking')
).toMatchObject({
level: 'unavailable-in-opencode',
label: 'Unavailable in OpenCode',
});
expect(getOpenCodeTeamModelRecommendation('openrouter/openai/gpt-audio-mini')).toMatchObject({
level: 'unavailable-in-opencode',
label: 'Unavailable in OpenCode',
});
expect(getOpenCodeTeamModelRecommendation('openrouter/amazon/nova-pro-v1')).toMatchObject({
level: 'unavailable-in-opencode',
label: 'Unavailable in OpenCode',
});
expect(getOpenCodeTeamModelRecommendation('openrouter/relace/relace-search')).toMatchObject({
level: 'unavailable-in-opencode',
label: 'Unavailable in OpenCode',
});
expect(getOpenCodeTeamModelRecommendation('openrouter/qwen/qwen-max')).toMatchObject({
level: 'unavailable-in-opencode',
label: 'Unavailable in OpenCode',
});
expect(
getOpenCodeTeamModelRecommendation('openrouter/mistralai/pixtral-large-2411')
).toMatchObject({
level: 'unavailable-in-opencode',
label: 'Unavailable in OpenCode',
});
expect(
getOpenCodeTeamModelRecommendation('openrouter/mistralai/mixtral-8x22b-instruct')
).toMatchObject({
level: 'unavailable-in-opencode',
label: 'Unavailable in OpenCode',
});
expect(getOpenCodeTeamModelRecommendation('openrouter/qwen/qwen3.5-35b-a3b')).toMatchObject({
level: 'unavailable-in-opencode',
label: 'Unavailable in OpenCode',
});
expect(getOpenCodeTeamModelRecommendation('openrouter/qwen/qwen3-30b-a3b')).toMatchObject({
level: 'unavailable-in-opencode',
label: 'Unavailable in OpenCode',
});
expect(getOpenCodeTeamModelRecommendation('openrouter/baidu/ernie-4.5-21b-a3b')).toMatchObject({
level: 'unavailable-in-opencode',
label: 'Unavailable in OpenCode',
});
expect(
getOpenCodeTeamModelRecommendation('openrouter/nousresearch/hermes-4-70b')
).toMatchObject({
level: 'unavailable-in-opencode',
label: 'Unavailable in OpenCode',
});
for (const modelId of [
'openrouter/openai/gpt-3.5-turbo-16k',
'openrouter/mistralai/mistral-large',
'openrouter/openai/o4-mini-deep-research',
'openrouter/ai21/jamba-large-1.7',
'openrouter/openai/o3',
'openrouter/openai/gpt-audio',
'openrouter/openai/gpt-4o-audio-preview',
'openrouter/openai/gpt-4o-2024-11-20',
'openrouter/openai/gpt-4o-2024-08-06',
'openrouter/amazon/nova-premier-v1',
'openrouter/anthropic/claude-3.7-sonnet:thinking',
'openrouter/openai/gpt-4o-2024-05-13',
'openrouter/~anthropic/claude-opus-latest',
'openrouter/openai/gpt-4-turbo',
'openrouter/openai/gpt-4-turbo-preview',
'openrouter/openai/gpt-4-1106-preview',
'openrouter/openai/o3-deep-research',
'openrouter/openai/o1',
'openrouter/openai/o3-pro',
'openrouter/anthropic/claude-opus-4.6-fast',
'openrouter/openai/gpt-5.5-pro',
'openrouter/openrouter/auto',
'openrouter/openrouter/pareto-code',
'openrouter/openrouter/bodybuilder',
'openrouter/baidu/qianfan-ocr-fast:free',
'openrouter/google/lyria-3-pro-preview',
'openrouter/ibm-granite/granite-4.0-h-micro',
'openrouter/microsoft/phi-4',
'openrouter/meta-llama/llama-guard-4-12b',
'openrouter/qwen/qwen-vl-plus',
'openrouter/deepseek/deepseek-r1-distill-qwen-32b',
'openrouter/google/lyria-3-clip-preview',
'openrouter/liquid/lfm-2-24b-a2b',
'openrouter/meta-llama/llama-3.2-1b-instruct',
'openrouter/bytedance/ui-tars-1.5-7b',
'openrouter/baidu/ernie-4.5-21b-a3b-thinking',
'openrouter/arcee-ai/spotlight',
'openrouter/meta-llama/llama-3.2-3b-instruct',
'openrouter/meta-llama/llama-guard-3-8b',
'openrouter/nousresearch/hermes-3-llama-3.1-70b',
'openrouter/allenai/olmo-3-32b-think',
'openrouter/tencent/hunyuan-a13b-instruct',
'openrouter/meta-llama/llama-4-maverick',
'openrouter/nvidia/nemotron-nano-12b-v2-vl',
'openrouter/thedrummer/cydonia-24b-v4.1',
'openrouter/microsoft/wizardlm-2-8x22b',
'openrouter/arcee-ai/coder-large',
'openrouter/thedrummer/skyfall-36b-v2',
'openrouter/baidu/ernie-4.5-300b-a47b',
'openrouter/sao10k/l3.3-euryale-70b',
]) {
expect(getOpenCodeTeamModelRecommendation(modelId)).toMatchObject({
level: 'unavailable-in-opencode',
label: 'Unavailable in OpenCode',
});
}
expect(isOpenCodeTeamModelRecommended('openrouter/qwen/qwen3-coder-plus')).toBe(false);
});
it('does not label noisy or unproven models as good or bad', () => {
expect(getOpenCodeTeamModelRecommendation('opencode/big-pickle')).toBeNull();
expect(getOpenCodeTeamModelRecommendation('openrouter/x-ai/grok-4.20')).toBeNull();
expect(getOpenCodeTeamModelRecommendation('openrouter/x-ai/grok-4.20-unknown')).toBeNull();
expect(getOpenCodeTeamModelRecommendation('')).toBeNull();
});
it('sorts recommended, limited, neutral, unavailable, and not-recommended routes by status', () => {
it('sorts tested, tested-with-limits, neutral, unavailable, and not-recommended routes by status', () => {
const models = [
'openrouter/openai/gpt-oss-20b:free',
'openrouter/qwen/qwen3-coder-plus',
'opencode/big-pickle',
'openrouter/openai/gpt-oss-120b:free',
'openrouter/qwen/qwen3-coder-flash',
'opencode/minimax-m2.5-free',
'openrouter/mistralai/codestral-2508',
'openrouter/anthropic/claude-sonnet-4.6',
];
expect(
[...models].sort((left, right) => compareOpenCodeTeamModelRecommendations(left, right))
).toEqual([
'openrouter/qwen/qwen3-coder-flash',
'openrouter/openai/gpt-oss-120b:free',
'openrouter/mistralai/codestral-2508',
'openrouter/anthropic/claude-sonnet-4.6',
'opencode/minimax-m2.5-free',
'opencode/big-pickle',
'openrouter/qwen/qwen3-coder-plus',
'openrouter/openai/gpt-oss-20b:free',