feat: improve opencode model verification ux
This commit is contained in:
parent
a0f1c92d18
commit
49982a1db8
12 changed files with 5182 additions and 234 deletions
|
|
@ -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 |
|
||||
|
||||
---
|
||||
|
|
|
|||
1059
docs/research/opencode-model-gauntlet-results.md
Normal file
1059
docs/research/opencode-model-gauntlet-results.md
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -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" />
|
||||
)}
|
||||
|
|
|
|||
|
|
@ -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
|
|
@ -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()}`;
|
||||
|
|
|
|||
2113
test/main/services/team/OpenCodeSemanticModelGauntlet.live.test.ts
Normal file
2113
test/main/services/team/OpenCodeSemanticModelGauntlet.live.test.ts
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -106,6 +106,7 @@ async function runModelScenario(input: {
|
|||
harness = await createOpenCodeLiveHarness({
|
||||
tempDir,
|
||||
selectedModel: input.model,
|
||||
projectPath,
|
||||
});
|
||||
|
||||
const progressEvents: TeamProvisioningProgress[] = [];
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
},
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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');
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
|
|||
|
|
@ -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',
|
||||
|
|
|
|||
Loading…
Reference in a new issue