mirror of
https://github.com/RooVetGit/Roo-Code.git
synced 2026-09-06 08:18:39 +00:00
More realistic allowlist screen (#43)
This commit is contained in:
parent
082ba3ce80
commit
ef42763ad9
20 changed files with 1925 additions and 243 deletions
|
|
@ -54,6 +54,7 @@
|
|||
"@tanstack/react-table": "^8.20.5",
|
||||
"class-variance-authority": "^0.7.1",
|
||||
"clsx": "^2.1.1",
|
||||
"cmdk": "^1.1.1",
|
||||
"date-fns": "^4.1.0",
|
||||
"drizzle-orm": "^0.43.1",
|
||||
"drizzle-zod": "^0.7.1",
|
||||
|
|
|
|||
21
pnpm-lock.yaml
generated
21
pnpm-lock.yaml
generated
|
|
@ -89,6 +89,9 @@ importers:
|
|||
clsx:
|
||||
specifier: ^2.1.1
|
||||
version: 2.1.1
|
||||
cmdk:
|
||||
specifier: ^1.1.1
|
||||
version: 1.1.1(@types/react@19.1.3)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
|
||||
date-fns:
|
||||
specifier: ^4.1.0
|
||||
version: 4.1.0
|
||||
|
|
@ -4368,6 +4371,12 @@ packages:
|
|||
resolution: {integrity: sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==}
|
||||
engines: {node: '>=6'}
|
||||
|
||||
cmdk@1.1.1:
|
||||
resolution: {integrity: sha512-Vsv7kFaXm+ptHDMZ7izaRsP70GgrW9NBNGswt9OZaVBLlE0SNpDq8eu/VGXyF9r7M0azK3Wy7OlYXsuyYLFzHg==}
|
||||
peerDependencies:
|
||||
react: ^18 || ^19 || ^19.0.0-rc
|
||||
react-dom: ^18 || ^19 || ^19.0.0-rc
|
||||
|
||||
co@4.6.0:
|
||||
resolution: {integrity: sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==}
|
||||
engines: {iojs: '>= 1.0.0', node: '>= 0.12.0'}
|
||||
|
|
@ -13426,6 +13435,18 @@ snapshots:
|
|||
|
||||
clsx@2.1.1: {}
|
||||
|
||||
cmdk@1.1.1(@types/react@19.1.3)(react-dom@19.1.0(react@19.1.0))(react@19.1.0):
|
||||
dependencies:
|
||||
'@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.3)(react@19.1.0)
|
||||
'@radix-ui/react-dialog': 1.1.13(@types/react@19.1.3)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
|
||||
'@radix-ui/react-id': 1.1.1(@types/react@19.1.3)(react@19.1.0)
|
||||
'@radix-ui/react-primitive': 2.1.2(@types/react@19.1.3)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
|
||||
react: 19.1.0
|
||||
react-dom: 19.1.0(react@19.1.0)
|
||||
transitivePeerDependencies:
|
||||
- '@types/react'
|
||||
- '@types/react-dom'
|
||||
|
||||
co@4.6.0: {}
|
||||
|
||||
collect-v8-coverage@1.0.2: {}
|
||||
|
|
|
|||
|
|
@ -1,91 +1,65 @@
|
|||
'use client';
|
||||
|
||||
import { useTranslations } from 'next-intl';
|
||||
import { useState } from 'react';
|
||||
import { useMemo, useState } from 'react';
|
||||
import { useQuery, useQueryClient } from '@tanstack/react-query';
|
||||
import { toast } from 'sonner';
|
||||
|
||||
import { type ProviderName } from '@roo-code/types';
|
||||
|
||||
import {
|
||||
type OrganizationAllowList,
|
||||
type OrganizationSettings,
|
||||
ORGANIZATION_ALLOW_ALL,
|
||||
} from '@/types';
|
||||
import { type OrganizationSettings, ORGANIZATION_ALLOW_ALL } from '@/types';
|
||||
import {
|
||||
getOrganizationSettings,
|
||||
updateOrganization,
|
||||
} from '@/actions/organizationSettings';
|
||||
import { Badge, Button, Checkbox, Label } from '@/components/ui';
|
||||
import MultipleSelector from '@/components/ui/multiple-selector';
|
||||
|
||||
type ProviderSetting = {
|
||||
allowAll: boolean;
|
||||
models?: string[];
|
||||
import openRouterModels from '@/lib/data/openrouter-models.json';
|
||||
import anthropicModels from '@/lib/data/anthropic.json';
|
||||
import geminiModels from '@/lib/data/gemini.json';
|
||||
import deepseekModels from '@/lib/data/deepseek.json';
|
||||
import openaiNativeModels from '@/lib/data/openai-native.json';
|
||||
import vertexModels from '@/lib/data/vertex.json';
|
||||
import bedrockModels from '@/lib/data/bedrock.json';
|
||||
import mistralModels from '@/lib/data/mistral.json';
|
||||
import requestyModels from '@/lib/data/requesty-models.json';
|
||||
import groqModels from '@/lib/data/groq.json';
|
||||
import xaiModels from '@/lib/data/xai.json';
|
||||
import unboundModels from '@/lib/data/unbound.json';
|
||||
import glamaModels from '@/lib/data/glama.json';
|
||||
import chutesModels from '@/lib/data/chutes.json';
|
||||
|
||||
type BaseProvider = {
|
||||
id: ProviderName;
|
||||
label: string;
|
||||
models: string[];
|
||||
};
|
||||
|
||||
// Define a type for providers record
|
||||
type ProvidersRecord = Record<ProviderName, ProviderSetting>;
|
||||
|
||||
// Provider metadata without state information
|
||||
const providerMetadata: {
|
||||
id: ProviderName;
|
||||
name: string;
|
||||
models: {
|
||||
id: string;
|
||||
name: string;
|
||||
}[];
|
||||
}[] = [
|
||||
{
|
||||
id: 'openai-native',
|
||||
name: 'OpenAI',
|
||||
models: [
|
||||
{ id: 'gpt-4', name: 'GPT-4' },
|
||||
{ id: 'gpt-3.5-turbo', name: 'GPT-3.5 Turbo' },
|
||||
{ id: 'gpt-4o', name: 'GPT-4o' },
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 'anthropic',
|
||||
name: 'Anthropic',
|
||||
models: [
|
||||
{ id: 'claude-3-opus-20240229', name: 'Claude 3 Opus' },
|
||||
{ id: 'claude-3-7-sonnet-20250219', name: 'Claude 3 Sonnet' },
|
||||
{
|
||||
id: 'claude-3-7-sonnet-20250219:thinking',
|
||||
name: 'Claude 3 Sonnet Thinking',
|
||||
},
|
||||
{ id: 'claude-3-haiku-20240307', name: 'Claude 3 Haiku' },
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 'openrouter',
|
||||
name: 'OpenRouter',
|
||||
models: [
|
||||
{ id: 'anthropic/claude-3-opus', name: 'Claude 3 Opus' },
|
||||
{ id: 'anthropic/claude-3.7-sonnet', name: 'Claude 3.7 Sonnet' },
|
||||
{
|
||||
id: 'anthropic/claude-3.7-sonnet:thinking',
|
||||
name: 'Claude 3.7 Sonnet Thinking',
|
||||
},
|
||||
{ id: 'anthropic/claude-3.5-haiku', name: 'Claude 3.5 Haiku' },
|
||||
{ id: 'openai/gpt-4', name: 'GPT-4' },
|
||||
{ id: 'openai/gpt-3.5-turbo', name: 'GPT-3.5 Turbo' },
|
||||
{ id: 'openai/gpt-4o', name: 'GPT-4o' },
|
||||
{
|
||||
id: 'google/gemini-2.5-flash-preview',
|
||||
name: 'Gemini 2.5 Flash Preview',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 'mistral',
|
||||
name: 'Mistral AI',
|
||||
models: [
|
||||
{ id: 'mistral-large-latest', name: 'Mistral Large' },
|
||||
{ id: 'mistral-small-latest', name: 'Mistral Small' },
|
||||
],
|
||||
},
|
||||
];
|
||||
const providers = (
|
||||
[
|
||||
{ id: 'openrouter', label: 'OpenRouter', models: openRouterModels },
|
||||
{ id: 'anthropic', label: 'Anthropic', models: anthropicModels },
|
||||
{ id: 'gemini', label: 'Google Gemini', models: geminiModels },
|
||||
{ id: 'deepseek', label: 'DeepSeek', models: deepseekModels },
|
||||
{ id: 'openai-native', label: 'OpenAI', models: openaiNativeModels },
|
||||
{ id: 'openai', label: 'OpenAI Compatible', models: [] },
|
||||
{ id: 'vertex', label: 'GCP Vertex AI', models: vertexModels },
|
||||
{ id: 'bedrock', label: 'Amazon Bedrock', models: bedrockModels },
|
||||
{ id: 'glama', label: 'Glama', models: glamaModels },
|
||||
{ id: 'vscode-lm', label: 'VS Code LM API', models: [] },
|
||||
{ id: 'mistral', label: 'Mistral', models: mistralModels },
|
||||
{ id: 'lmstudio', label: 'LM Studio', models: [] },
|
||||
{ id: 'ollama', label: 'Ollama', models: [] },
|
||||
{ id: 'unbound', label: 'Unbound', models: unboundModels },
|
||||
{ id: 'requesty', label: 'Requesty', models: requestyModels },
|
||||
{ id: 'human-relay', label: 'Human Relay', models: [] },
|
||||
{ id: 'xai', label: 'xAI (Grok)', models: xaiModels },
|
||||
{ id: 'groq', label: 'Groq', models: groqModels },
|
||||
{ id: 'chutes', label: 'Chutes AI', models: chutesModels },
|
||||
{ id: 'litellm', label: 'LiteLLM', models: [] },
|
||||
] satisfies BaseProvider[]
|
||||
).sort((a, b) => a.label.localeCompare(b.label));
|
||||
|
||||
type ProviderWhitelistFormProps = {
|
||||
orgSettings: OrganizationSettings;
|
||||
|
|
@ -98,162 +72,115 @@ const ProviderWhitelistForm = ({
|
|||
}: ProviderWhitelistFormProps) => {
|
||||
const t = useTranslations('ProviderWhitelist');
|
||||
|
||||
const [allowList, setAllowList] = useState<OrganizationAllowList>(
|
||||
orgSettings?.allowList || ORGANIZATION_ALLOW_ALL,
|
||||
const fullProviderMetadata = useMemo(
|
||||
() =>
|
||||
providers.map((provider) => {
|
||||
const models = orgSettings.allowList.providers[provider.id]?.models;
|
||||
if (models) {
|
||||
const providerModels = new Set(provider.models);
|
||||
const difference = models.filter(
|
||||
(model) => !providerModels.has(model),
|
||||
);
|
||||
if (difference.length > 0) {
|
||||
return {
|
||||
...provider,
|
||||
models: [...provider.models, ...difference],
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
return provider;
|
||||
}),
|
||||
[orgSettings],
|
||||
);
|
||||
|
||||
const [allowAll, setAllowAll] = useState(orgSettings.allowList.allowAll);
|
||||
const [providerAllowAll, setProviderAllowAll] = useState(
|
||||
Object.entries(orgSettings.allowList.providers).reduce(
|
||||
(acc, [provider, providerSettings]) => {
|
||||
if (providerSettings.allowAll) {
|
||||
acc.add(provider);
|
||||
}
|
||||
return acc;
|
||||
},
|
||||
new Set<string>(),
|
||||
),
|
||||
);
|
||||
const [providerModels, setProviderModels] = useState(
|
||||
fullProviderMetadata.reduce((acc, meta) => {
|
||||
acc.set(meta.id, orgSettings.allowList.providers[meta.id]?.models || []);
|
||||
return acc;
|
||||
}, new Map<ProviderName, string[]>()),
|
||||
);
|
||||
|
||||
const [hasChanges, setHasChanges] = useState(false);
|
||||
const [isSaving, setIsSaving] = useState(false);
|
||||
|
||||
const isAllowAllChecked = () => allowList.allowAll;
|
||||
|
||||
const isProviderChecked = (providerId: ProviderName) => {
|
||||
if (allowList.allowAll) return true;
|
||||
return allowList.providers[providerId]?.allowAll || false;
|
||||
};
|
||||
|
||||
const isModelChecked = (providerId: ProviderName, modelId: string) => {
|
||||
if (allowList.allowAll) return true;
|
||||
const provider = allowList.providers[providerId];
|
||||
if (!provider) return false;
|
||||
if (provider.allowAll) return true;
|
||||
return provider.models?.includes(modelId) || false;
|
||||
};
|
||||
const isProviderAllowAll = (providerId: ProviderName) =>
|
||||
providerAllowAll.has(providerId);
|
||||
|
||||
const toggleAllowAll = () => {
|
||||
const newAllowAll = !allowList.allowAll;
|
||||
|
||||
if (newAllowAll) {
|
||||
setAllowList({ allowAll: true, providers: {} });
|
||||
} else {
|
||||
const newProviders: Partial<ProvidersRecord> = {};
|
||||
|
||||
providerMetadata.forEach((provider) => {
|
||||
newProviders[provider.id] = { allowAll: true };
|
||||
});
|
||||
|
||||
setAllowList({
|
||||
allowAll: false,
|
||||
providers: newProviders,
|
||||
});
|
||||
}
|
||||
setAllowAll(!allowAll);
|
||||
|
||||
setHasChanges(true);
|
||||
};
|
||||
|
||||
const toggleProvider = (providerId: ProviderName) => {
|
||||
if (allowList.allowAll) {
|
||||
const newProviders: Partial<ProvidersRecord> = {};
|
||||
|
||||
providerMetadata.forEach((provider) => {
|
||||
if (provider.id !== providerId) {
|
||||
newProviders[provider.id] = { allowAll: true };
|
||||
}
|
||||
});
|
||||
|
||||
setAllowList({
|
||||
allowAll: false,
|
||||
providers: newProviders,
|
||||
});
|
||||
const newProviderAllowAll = new Set(providerAllowAll);
|
||||
if (providerAllowAll.has(providerId)) {
|
||||
newProviderAllowAll.delete(providerId);
|
||||
} else {
|
||||
const newProviders = { ...allowList.providers };
|
||||
const providersRecord = newProviders;
|
||||
const isCurrentlyEnabled = isProviderChecked(providerId);
|
||||
|
||||
if (isCurrentlyEnabled) {
|
||||
if (providersRecord[providerId]) {
|
||||
providersRecord[providerId] = {
|
||||
...providersRecord[providerId],
|
||||
allowAll: false,
|
||||
models: [],
|
||||
};
|
||||
}
|
||||
} else {
|
||||
providersRecord[providerId] = { allowAll: true };
|
||||
}
|
||||
|
||||
setAllowList({ ...allowList, providers: newProviders });
|
||||
newProviderAllowAll.add(providerId);
|
||||
}
|
||||
setProviderAllowAll(newProviderAllowAll);
|
||||
|
||||
setHasChanges(true);
|
||||
};
|
||||
|
||||
const toggleModel = (providerId: ProviderName, modelId: string) => {
|
||||
if (allowList.allowAll || isProviderChecked(providerId)) {
|
||||
const newProviders = { ...allowList.providers };
|
||||
const providersRecord = newProviders;
|
||||
|
||||
if (allowList.allowAll) {
|
||||
providerMetadata.forEach((provider) => {
|
||||
providersRecord[provider.id] = { allowAll: true };
|
||||
});
|
||||
|
||||
const provider = providerMetadata.find((p) => p.id === providerId);
|
||||
if (provider) {
|
||||
const models = provider.models
|
||||
.filter((m) => m.id !== modelId)
|
||||
.map((m) => m.id);
|
||||
|
||||
providersRecord[providerId] = { allowAll: false, models };
|
||||
}
|
||||
|
||||
setAllowList({ allowAll: false, providers: newProviders });
|
||||
} else {
|
||||
const provider = providerMetadata.find((p) => p.id === providerId);
|
||||
if (provider) {
|
||||
const models = provider.models
|
||||
.filter((m) => m.id !== modelId)
|
||||
.map((m) => m.id);
|
||||
|
||||
providersRecord[providerId] = { allowAll: false, models };
|
||||
setAllowList({ ...allowList, providers: newProviders });
|
||||
}
|
||||
}
|
||||
} else {
|
||||
const newProviders = { ...allowList.providers };
|
||||
const providersRecord = newProviders;
|
||||
const provider = providersRecord[providerId] || {
|
||||
allowAll: false,
|
||||
models: [],
|
||||
};
|
||||
const models = provider.models || [];
|
||||
|
||||
const isCurrentlyEnabled = models.includes(modelId);
|
||||
|
||||
if (isCurrentlyEnabled) {
|
||||
providersRecord[providerId] = {
|
||||
...provider,
|
||||
models: models.filter((m: string) => m !== modelId),
|
||||
};
|
||||
} else {
|
||||
providersRecord[providerId] = {
|
||||
...provider,
|
||||
models: [...models, modelId],
|
||||
};
|
||||
|
||||
const allModels =
|
||||
providerMetadata
|
||||
.find((p) => p.id === providerId)
|
||||
?.models.map((m) => m.id) || [];
|
||||
const enabledModels = providersRecord[providerId].models || [];
|
||||
|
||||
if (
|
||||
allModels.length === enabledModels.length &&
|
||||
allModels.every((m) => enabledModels.includes(m))
|
||||
) {
|
||||
providersRecord[providerId] = { allowAll: true };
|
||||
}
|
||||
}
|
||||
|
||||
setAllowList({ ...allowList, providers: newProviders });
|
||||
}
|
||||
|
||||
const setModels = (providerId: ProviderName, models: string[]) => {
|
||||
const newProviderModels = new Map(providerModels);
|
||||
newProviderModels.set(providerId, models);
|
||||
setProviderModels(newProviderModels);
|
||||
setHasChanges(true);
|
||||
};
|
||||
|
||||
const saveChanges = async () => {
|
||||
setIsSaving(true);
|
||||
try {
|
||||
let allowList;
|
||||
if (allowAll) {
|
||||
allowList = ORGANIZATION_ALLOW_ALL;
|
||||
} else {
|
||||
allowList = {
|
||||
allowAll: false,
|
||||
providers: fullProviderMetadata.reduce(
|
||||
(acc, meta) => {
|
||||
if (providerAllowAll.has(meta.id)) {
|
||||
acc[meta.id] = {
|
||||
allowAll: true,
|
||||
};
|
||||
} else {
|
||||
const models = providerModels.get(meta.id);
|
||||
if (models && models.length > 0) {
|
||||
acc[meta.id] = {
|
||||
allowAll: false,
|
||||
models: models,
|
||||
};
|
||||
}
|
||||
}
|
||||
return acc;
|
||||
},
|
||||
{} as Record<
|
||||
ProviderName,
|
||||
{
|
||||
allowAll: boolean;
|
||||
models?: string[];
|
||||
}
|
||||
>,
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
const result = await updateOrganization({
|
||||
allowList: allowList,
|
||||
});
|
||||
|
|
@ -291,7 +218,7 @@ const ProviderWhitelistForm = ({
|
|||
<div className="mb-4 flex items-center space-x-2">
|
||||
<Checkbox
|
||||
id="allow-all-providers"
|
||||
checked={isAllowAllChecked()}
|
||||
checked={allowAll}
|
||||
onCheckedChange={toggleAllowAll}
|
||||
disabled={isSaving}
|
||||
/>
|
||||
|
|
@ -302,46 +229,49 @@ const ProviderWhitelistForm = ({
|
|||
Allow All Providers
|
||||
</Label>
|
||||
</div>
|
||||
<div className={isAllowAllChecked() ? 'opacity-50' : ''}>
|
||||
{providerMetadata.map((provider) => (
|
||||
<div className={allowAll ? 'opacity-50' : ''}>
|
||||
{fullProviderMetadata.map((provider) => (
|
||||
<div key={provider.id} className="mb-4">
|
||||
<div className="mb-2 flex items-center space-x-2">
|
||||
<Checkbox
|
||||
id={`provider-${provider.id}`}
|
||||
checked={isProviderChecked(provider.id)}
|
||||
disabled={isAllowAllChecked() || isSaving}
|
||||
onCheckedChange={() => toggleProvider(provider.id)}
|
||||
/>
|
||||
<Label
|
||||
htmlFor={`provider-${provider.id}`}
|
||||
className="text-sm font-medium"
|
||||
>
|
||||
{provider.name}
|
||||
</Label>
|
||||
<div className="mb-2 flex items-center justify-between">
|
||||
<div className="text-sm font-medium">{provider.label}</div>
|
||||
<div className="flex items-center space-x-2">
|
||||
<Label
|
||||
htmlFor={`provider-${provider.id}`}
|
||||
className="text-sm"
|
||||
>
|
||||
Allow all models
|
||||
</Label>
|
||||
<Checkbox
|
||||
id={`provider-${provider.id}`}
|
||||
checked={isProviderAllowAll(provider.id)}
|
||||
disabled={allowAll || isSaving}
|
||||
onCheckedChange={() => toggleProvider(provider.id)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="ml-6 space-y-2">
|
||||
{provider.models.map((model) => (
|
||||
<div key={model.id} className="flex items-center space-x-2">
|
||||
<Checkbox
|
||||
id={`model-${model.id}`}
|
||||
checked={isModelChecked(provider.id, model.id)}
|
||||
disabled={
|
||||
isAllowAllChecked() ||
|
||||
isProviderChecked(provider.id) ||
|
||||
isSaving
|
||||
}
|
||||
onCheckedChange={() =>
|
||||
toggleModel(provider.id, model.id)
|
||||
}
|
||||
/>
|
||||
<Label
|
||||
htmlFor={`model-${model.id}`}
|
||||
className={`text-xs ${isAllowAllChecked() || !isProviderChecked(provider.id) ? 'text-muted-foreground' : ''}`}
|
||||
>
|
||||
{model.name}
|
||||
</Label>
|
||||
</div>
|
||||
))}
|
||||
<div className="max-w-full">
|
||||
<MultipleSelector
|
||||
defaultOptions={provider.models.map((model) => ({
|
||||
label: model,
|
||||
value: model,
|
||||
disable: providerModels.get(provider.id)?.includes(model),
|
||||
}))}
|
||||
value={(providerModels.get(provider.id) || []).map(
|
||||
(model) => ({
|
||||
label: model,
|
||||
value: model,
|
||||
}),
|
||||
)}
|
||||
creatable
|
||||
disabled={allowAll || isSaving}
|
||||
placeholder="Pick models..."
|
||||
onChange={(options) =>
|
||||
setModels(
|
||||
provider.id,
|
||||
options.map((option) => option.value),
|
||||
)
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
|
|
|
|||
134
src/components/ui/command.tsx
Normal file
134
src/components/ui/command.tsx
Normal file
|
|
@ -0,0 +1,134 @@
|
|||
"use client"
|
||||
|
||||
import * as React from "react"
|
||||
import { Command as CommandPrimitive } from "cmdk"
|
||||
import { SearchIcon } from "lucide-react"
|
||||
|
||||
import { cn } from "@/lib/utils"
|
||||
import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle } from "@/components/ui/dialog"
|
||||
|
||||
function Command({ className, ...props }: React.ComponentProps<typeof CommandPrimitive>) {
|
||||
return (
|
||||
<CommandPrimitive
|
||||
data-slot="command"
|
||||
className={cn(
|
||||
"bg-popover text-popover-foreground flex h-full w-full flex-col overflow-hidden rounded-sm",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
function CommandDialog({
|
||||
title = "Command Palette",
|
||||
description = "Search for a command to run...",
|
||||
children,
|
||||
...props
|
||||
}: React.ComponentProps<typeof Dialog> & {
|
||||
title?: string
|
||||
description?: string
|
||||
}) {
|
||||
return (
|
||||
<Dialog {...props}>
|
||||
<DialogHeader className="sr-only">
|
||||
<DialogTitle>{title}</DialogTitle>
|
||||
<DialogDescription>{description}</DialogDescription>
|
||||
</DialogHeader>
|
||||
<DialogContent className="overflow-hidden p-0">
|
||||
<Command className="[&_[cmdk-group-heading]]:text-muted-foreground **:data-[slot=command-input-wrapper]:h-12 [&_[cmdk-group-heading]]:px-2 [&_[cmdk-group-heading]]:font-medium [&_[cmdk-group]]:px-2 [&_[cmdk-group]:not([hidden])_~[cmdk-group]]:pt-0 [&_[cmdk-input-wrapper]_svg]:h-5 [&_[cmdk-input-wrapper]_svg]:w-5 [&_[cmdk-input]]:h-12 [&_[cmdk-item]]:px-2 [&_[cmdk-item]]:py-3 [&_[cmdk-item]_svg]:h-5 [&_[cmdk-item]_svg]:w-5">
|
||||
{children}
|
||||
</Command>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
)
|
||||
}
|
||||
|
||||
function CommandInput({ className, ...props }: React.ComponentProps<typeof CommandPrimitive.Input>) {
|
||||
return (
|
||||
<div data-slot="command-input-wrapper" className="flex h-9 items-center gap-2 border-b px-3">
|
||||
<SearchIcon className="size-4 shrink-0 opacity-50" />
|
||||
<CommandPrimitive.Input
|
||||
data-slot="command-input"
|
||||
className={cn(
|
||||
"placeholder:text-muted-foreground flex h-10 w-full rounded-sm bg-transparent py-3 outline-hidden disabled:cursor-not-allowed disabled:opacity-50",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
function CommandList({ className, ...props }: React.ComponentProps<typeof CommandPrimitive.List>) {
|
||||
return (
|
||||
<CommandPrimitive.List
|
||||
data-slot="command-list"
|
||||
className={cn("max-h-[300px] scroll-py-1 overflow-x-hidden overflow-y-auto", className)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
function CommandEmpty({ ...props }: React.ComponentProps<typeof CommandPrimitive.Empty>) {
|
||||
return <CommandPrimitive.Empty data-slot="command-empty" className="py-6 text-center" {...props} />
|
||||
}
|
||||
|
||||
function CommandGroup({ className, ...props }: React.ComponentProps<typeof CommandPrimitive.Group>) {
|
||||
return (
|
||||
<CommandPrimitive.Group
|
||||
data-slot="command-group"
|
||||
className={cn(
|
||||
"text-foreground [&_[cmdk-group-heading]]:text-muted-foreground overflow-hidden p-1 [&_[cmdk-group-heading]]:px-2 [&_[cmdk-group-heading]]:py-1.5 [&_[cmdk-group-heading]]:text-xs [&_[cmdk-group-heading]]:font-medium",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
function CommandSeparator({ className, ...props }: React.ComponentProps<typeof CommandPrimitive.Separator>) {
|
||||
return (
|
||||
<CommandPrimitive.Separator
|
||||
data-slot="command-separator"
|
||||
className={cn("bg-accent/5 -mx-1 h-px", className)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
function CommandItem({ className, ...props }: React.ComponentProps<typeof CommandPrimitive.Item>) {
|
||||
return (
|
||||
<CommandPrimitive.Item
|
||||
data-slot="command-item"
|
||||
className={cn(
|
||||
"data-[selected=true]:bg-accent data-[selected=true]:text-accent-foreground [&_svg:not([class*='text-'])]:text-muted-foreground relative flex cursor-default items-center gap-2 rounded-xs px-2 py-1.5 outline-hidden select-none data-[disabled=true]:pointer-events-none data-[disabled=true]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
|
||||
"text-foreground active:opacity-80 cursor-pointer group",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
function CommandShortcut({ className, ...props }: React.ComponentProps<"span">) {
|
||||
return (
|
||||
<span
|
||||
data-slot="command-shortcut"
|
||||
className={cn("text-muted-foreground ml-auto text-xs tracking-widest", className)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
export {
|
||||
Command,
|
||||
CommandDialog,
|
||||
CommandInput,
|
||||
CommandList,
|
||||
CommandEmpty,
|
||||
CommandGroup,
|
||||
CommandItem,
|
||||
CommandShortcut,
|
||||
CommandSeparator,
|
||||
}
|
||||
110
src/components/ui/dialog.tsx
Normal file
110
src/components/ui/dialog.tsx
Normal file
|
|
@ -0,0 +1,110 @@
|
|||
"use client"
|
||||
|
||||
import * as React from "react"
|
||||
import * as DialogPrimitive from "@radix-ui/react-dialog"
|
||||
import { XIcon } from "lucide-react"
|
||||
|
||||
import { cn } from "@/lib/utils"
|
||||
|
||||
function Dialog({ ...props }: React.ComponentProps<typeof DialogPrimitive.Root>) {
|
||||
return <DialogPrimitive.Root data-slot="dialog" {...props} />
|
||||
}
|
||||
|
||||
function DialogTrigger({ ...props }: React.ComponentProps<typeof DialogPrimitive.Trigger>) {
|
||||
return <DialogPrimitive.Trigger data-slot="dialog-trigger" {...props} />
|
||||
}
|
||||
|
||||
function DialogPortal({ ...props }: React.ComponentProps<typeof DialogPrimitive.Portal>) {
|
||||
return <DialogPrimitive.Portal data-slot="dialog-portal" {...props} />
|
||||
}
|
||||
|
||||
function DialogClose({ ...props }: React.ComponentProps<typeof DialogPrimitive.Close>) {
|
||||
return <DialogPrimitive.Close data-slot="dialog-close" {...props} />
|
||||
}
|
||||
|
||||
function DialogOverlay({ className, ...props }: React.ComponentProps<typeof DialogPrimitive.Overlay>) {
|
||||
return (
|
||||
<DialogPrimitive.Overlay
|
||||
data-slot="dialog-overlay"
|
||||
className={cn(
|
||||
"data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 fixed inset-0 z-50 bg-black/50",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
function DialogContent({ className, children, ...props }: React.ComponentProps<typeof DialogPrimitive.Content>) {
|
||||
return (
|
||||
<DialogPortal data-slot="dialog-portal">
|
||||
<DialogOverlay />
|
||||
<DialogPrimitive.Content
|
||||
data-slot="dialog-content"
|
||||
className={cn(
|
||||
"bg-background data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 fixed top-[50%] left-[50%] z-50 grid w-full max-w-[calc(100%-2rem)] translate-x-[-50%] translate-y-[-50%] gap-4 rounded-lg border p-6 shadow-lg duration-200 sm:max-w-lg",
|
||||
className,
|
||||
)}
|
||||
{...props}>
|
||||
{children}
|
||||
<DialogPrimitive.Close className="ring-offset-background focus:ring-ring data-[state=open]:bg-accent data-[state=open]:text-muted-foreground absolute top-4 right-4 rounded-xs opacity-70 transition-opacity hover:opacity-100 focus:ring-2 focus:ring-offset-2 focus:outline-hidden disabled:pointer-events-none [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4">
|
||||
<XIcon />
|
||||
<span className="sr-only">Close</span>
|
||||
</DialogPrimitive.Close>
|
||||
</DialogPrimitive.Content>
|
||||
</DialogPortal>
|
||||
)
|
||||
}
|
||||
|
||||
function DialogHeader({ className, ...props }: React.ComponentProps<"div">) {
|
||||
return (
|
||||
<div
|
||||
data-slot="dialog-header"
|
||||
className={cn("flex flex-col gap-2 text-center sm:text-left", className)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
function DialogFooter({ className, ...props }: React.ComponentProps<"div">) {
|
||||
return (
|
||||
<div
|
||||
data-slot="dialog-footer"
|
||||
className={cn("flex flex-col-reverse gap-2 sm:flex-row sm:justify-end", className)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
function DialogTitle({ className, ...props }: React.ComponentProps<typeof DialogPrimitive.Title>) {
|
||||
return (
|
||||
<DialogPrimitive.Title
|
||||
data-slot="dialog-title"
|
||||
className={cn("text-lg leading-none font-semibold", className)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
function DialogDescription({ className, ...props }: React.ComponentProps<typeof DialogPrimitive.Description>) {
|
||||
return (
|
||||
<DialogPrimitive.Description
|
||||
data-slot="dialog-description"
|
||||
className={cn("text-muted-foreground text-sm", className)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
export {
|
||||
Dialog,
|
||||
DialogClose,
|
||||
DialogContent,
|
||||
DialogDescription,
|
||||
DialogFooter,
|
||||
DialogHeader,
|
||||
DialogOverlay,
|
||||
DialogPortal,
|
||||
DialogTitle,
|
||||
DialogTrigger,
|
||||
}
|
||||
637
src/components/ui/multiple-selector.tsx
Normal file
637
src/components/ui/multiple-selector.tsx
Normal file
|
|
@ -0,0 +1,637 @@
|
|||
'use client';
|
||||
|
||||
import { Command as CommandPrimitive, useCommandState } from 'cmdk';
|
||||
import { X } from 'lucide-react';
|
||||
import * as React from 'react';
|
||||
import { forwardRef, useEffect } from 'react';
|
||||
|
||||
import { Badge } from '@/components/ui/badge';
|
||||
import {
|
||||
Command,
|
||||
CommandGroup,
|
||||
CommandItem,
|
||||
CommandList,
|
||||
} from '@/components/ui/command';
|
||||
import { cn } from '@/lib/utils';
|
||||
|
||||
export interface Option {
|
||||
value: string;
|
||||
label: string;
|
||||
disable?: boolean;
|
||||
/** fixed option that can't be removed. */
|
||||
fixed?: boolean;
|
||||
/** Group the options by providing key. */
|
||||
[key: string]: string | boolean | undefined;
|
||||
}
|
||||
interface GroupOption {
|
||||
[key: string]: Option[];
|
||||
}
|
||||
|
||||
interface MultipleSelectorProps {
|
||||
value?: Option[];
|
||||
defaultOptions?: Option[];
|
||||
/** manually controlled options */
|
||||
options?: Option[];
|
||||
placeholder?: string;
|
||||
/** Loading component. */
|
||||
loadingIndicator?: React.ReactNode;
|
||||
/** Empty component. */
|
||||
emptyIndicator?: React.ReactNode;
|
||||
/** Debounce time for async search. Only work with `onSearch`. */
|
||||
delay?: number;
|
||||
/**
|
||||
* Only work with `onSearch` prop. Trigger search when `onFocus`.
|
||||
* For example, when user click on the input, it will trigger the search to get initial options.
|
||||
**/
|
||||
triggerSearchOnFocus?: boolean;
|
||||
/** async search */
|
||||
onSearch?: (value: string) => Promise<Option[]>;
|
||||
/**
|
||||
* sync search. This search will not showing loadingIndicator.
|
||||
* The rest props are the same as async search.
|
||||
* i.e.: creatable, groupBy, delay.
|
||||
**/
|
||||
onSearchSync?: (value: string) => Option[];
|
||||
onChange?: (options: Option[]) => void;
|
||||
/** Limit the maximum number of selected options. */
|
||||
maxSelected?: number;
|
||||
/** When the number of selected options exceeds the limit, the onMaxSelected will be called. */
|
||||
onMaxSelected?: (maxLimit: number) => void;
|
||||
/** Hide the placeholder when there are options selected. */
|
||||
hidePlaceholderWhenSelected?: boolean;
|
||||
disabled?: boolean;
|
||||
/** Group the options base on provided key. */
|
||||
groupBy?: string;
|
||||
className?: string;
|
||||
badgeClassName?: string;
|
||||
/**
|
||||
* First item selected is a default behavior by cmdk. That is why the default is true.
|
||||
* This is a workaround solution by add a dummy item.
|
||||
*
|
||||
* @reference: https://github.com/pacocoursey/cmdk/issues/171
|
||||
*/
|
||||
selectFirstItem?: boolean;
|
||||
/** Allow user to create option when there is no option matched. */
|
||||
creatable?: boolean;
|
||||
/** Props of `Command` */
|
||||
commandProps?: React.ComponentPropsWithoutRef<typeof Command>;
|
||||
/** Props of `CommandInput` */
|
||||
inputProps?: Omit<
|
||||
React.ComponentPropsWithoutRef<typeof CommandPrimitive.Input>,
|
||||
'value' | 'placeholder' | 'disabled'
|
||||
>;
|
||||
/** hide the clear all button. */
|
||||
hideClearAllButton?: boolean;
|
||||
}
|
||||
|
||||
export interface MultipleSelectorRef {
|
||||
selectedValue: Option[];
|
||||
input: HTMLInputElement;
|
||||
focus: () => void;
|
||||
reset: () => void;
|
||||
}
|
||||
|
||||
export function useDebounce<T>(value: T, delay?: number): T {
|
||||
const [debouncedValue, setDebouncedValue] = React.useState<T>(value);
|
||||
|
||||
useEffect(() => {
|
||||
const timer = setTimeout(() => setDebouncedValue(value), delay || 500);
|
||||
|
||||
return () => {
|
||||
clearTimeout(timer);
|
||||
};
|
||||
}, [value, delay]);
|
||||
|
||||
return debouncedValue;
|
||||
}
|
||||
|
||||
function transToGroupOption(options: Option[], groupBy?: string) {
|
||||
if (options.length === 0) {
|
||||
return {};
|
||||
}
|
||||
if (!groupBy) {
|
||||
return {
|
||||
'': options,
|
||||
};
|
||||
}
|
||||
|
||||
const groupOption: GroupOption = {};
|
||||
options.forEach((option) => {
|
||||
const key = (option[groupBy] as string) || '';
|
||||
if (!groupOption[key]) {
|
||||
groupOption[key] = [];
|
||||
}
|
||||
groupOption[key].push(option);
|
||||
});
|
||||
return groupOption;
|
||||
}
|
||||
|
||||
function removePickedOption(groupOption: GroupOption, picked: Option[]) {
|
||||
const cloneOption = JSON.parse(JSON.stringify(groupOption)) as GroupOption;
|
||||
|
||||
for (const [key, value] of Object.entries(cloneOption)) {
|
||||
cloneOption[key] = value.filter(
|
||||
(val) => !picked.find((p) => p.value === val.value),
|
||||
);
|
||||
}
|
||||
return cloneOption;
|
||||
}
|
||||
|
||||
function isOptionsExist(groupOption: GroupOption, targetOption: Option[]) {
|
||||
for (const [, value] of Object.entries(groupOption)) {
|
||||
if (
|
||||
value.some((option) => targetOption.find((p) => p.value === option.value))
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* The `CommandEmpty` of shadcn/ui will cause the cmdk empty not rendering correctly.
|
||||
* So we create one and copy the `Empty` implementation from `cmdk`.
|
||||
*
|
||||
* @reference: https://github.com/hsuanyi-chou/shadcn-ui-expansions/issues/34#issuecomment-1949561607
|
||||
**/
|
||||
const CommandEmpty = forwardRef<
|
||||
HTMLDivElement,
|
||||
React.ComponentProps<typeof CommandPrimitive.Empty>
|
||||
>(({ className, ...props }, forwardedRef) => {
|
||||
const render = useCommandState((state) => state.filtered.count === 0);
|
||||
|
||||
if (!render) return null;
|
||||
|
||||
return (
|
||||
<div
|
||||
ref={forwardedRef}
|
||||
className={cn('py-6 text-center text-sm', className)}
|
||||
// eslint-disable-next-line react/no-unknown-property
|
||||
cmdk-empty=""
|
||||
role="presentation"
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
||||
CommandEmpty.displayName = 'CommandEmpty';
|
||||
|
||||
const MultipleSelector = React.forwardRef<
|
||||
MultipleSelectorRef,
|
||||
MultipleSelectorProps
|
||||
>(
|
||||
(
|
||||
{
|
||||
value,
|
||||
onChange,
|
||||
placeholder,
|
||||
defaultOptions: arrayDefaultOptions = [],
|
||||
options: arrayOptions,
|
||||
delay,
|
||||
onSearch,
|
||||
onSearchSync,
|
||||
loadingIndicator,
|
||||
emptyIndicator,
|
||||
maxSelected = Number.MAX_SAFE_INTEGER,
|
||||
onMaxSelected,
|
||||
hidePlaceholderWhenSelected,
|
||||
disabled,
|
||||
groupBy,
|
||||
className,
|
||||
badgeClassName,
|
||||
selectFirstItem = true,
|
||||
creatable = false,
|
||||
triggerSearchOnFocus = false,
|
||||
commandProps,
|
||||
inputProps,
|
||||
hideClearAllButton = false,
|
||||
}: MultipleSelectorProps,
|
||||
ref: React.Ref<MultipleSelectorRef>,
|
||||
) => {
|
||||
const inputRef = React.useRef<HTMLInputElement>(null);
|
||||
const [open, setOpen] = React.useState(false);
|
||||
const [onScrollbar, setOnScrollbar] = React.useState(false);
|
||||
const [isLoading, setIsLoading] = React.useState(false);
|
||||
const dropdownRef = React.useRef<HTMLDivElement>(null); // Added this
|
||||
|
||||
const [selected, setSelected] = React.useState<Option[]>(value || []);
|
||||
const [options, setOptions] = React.useState<GroupOption>(
|
||||
transToGroupOption(arrayDefaultOptions, groupBy),
|
||||
);
|
||||
const [inputValue, setInputValue] = React.useState('');
|
||||
const debouncedSearchTerm = useDebounce(inputValue, delay || 500);
|
||||
|
||||
React.useImperativeHandle(
|
||||
ref,
|
||||
() => ({
|
||||
selectedValue: [...selected],
|
||||
input: inputRef.current as HTMLInputElement,
|
||||
focus: () => inputRef?.current?.focus(),
|
||||
reset: () => setSelected([]),
|
||||
}),
|
||||
[selected],
|
||||
);
|
||||
|
||||
const handleClickOutside = (event: MouseEvent | TouchEvent) => {
|
||||
if (
|
||||
dropdownRef.current &&
|
||||
!dropdownRef.current.contains(event.target as Node) &&
|
||||
inputRef.current &&
|
||||
!inputRef.current.contains(event.target as Node)
|
||||
) {
|
||||
setOpen(false);
|
||||
inputRef.current.blur();
|
||||
}
|
||||
};
|
||||
|
||||
const handleUnselect = React.useCallback(
|
||||
(option: Option) => {
|
||||
const newOptions = selected.filter((s) => s.value !== option.value);
|
||||
setSelected(newOptions);
|
||||
onChange?.(newOptions);
|
||||
},
|
||||
[onChange, selected],
|
||||
);
|
||||
|
||||
const handleKeyDown = React.useCallback(
|
||||
(e: React.KeyboardEvent<HTMLDivElement>) => {
|
||||
const input = inputRef.current;
|
||||
if (input) {
|
||||
if (e.key === 'Delete' || e.key === 'Backspace') {
|
||||
if (input.value === '' && selected.length > 0) {
|
||||
const lastSelectOption = selected[selected.length - 1];
|
||||
// If last item is fixed, we should not remove it.
|
||||
if (lastSelectOption && !lastSelectOption.fixed) {
|
||||
handleUnselect(lastSelectOption);
|
||||
}
|
||||
}
|
||||
}
|
||||
// This is not a default behavior of the <input /> field
|
||||
if (e.key === 'Escape') {
|
||||
input.blur();
|
||||
}
|
||||
}
|
||||
},
|
||||
[handleUnselect, selected],
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
if (open) {
|
||||
document.addEventListener('mousedown', handleClickOutside);
|
||||
document.addEventListener('touchend', handleClickOutside);
|
||||
} else {
|
||||
document.removeEventListener('mousedown', handleClickOutside);
|
||||
document.removeEventListener('touchend', handleClickOutside);
|
||||
}
|
||||
|
||||
return () => {
|
||||
document.removeEventListener('mousedown', handleClickOutside);
|
||||
document.removeEventListener('touchend', handleClickOutside);
|
||||
};
|
||||
}, [open]);
|
||||
|
||||
useEffect(() => {
|
||||
if (value) {
|
||||
setSelected(value);
|
||||
}
|
||||
}, [value]);
|
||||
|
||||
useEffect(() => {
|
||||
/** If `onSearch` is provided, do not trigger options updated. */
|
||||
if (!arrayOptions || onSearch) {
|
||||
return;
|
||||
}
|
||||
const newOption = transToGroupOption(arrayOptions || [], groupBy);
|
||||
if (JSON.stringify(newOption) !== JSON.stringify(options)) {
|
||||
setOptions(newOption);
|
||||
}
|
||||
}, [arrayDefaultOptions, arrayOptions, groupBy, onSearch, options]);
|
||||
|
||||
useEffect(() => {
|
||||
/** sync search */
|
||||
|
||||
const doSearchSync = () => {
|
||||
const res = onSearchSync?.(debouncedSearchTerm);
|
||||
setOptions(transToGroupOption(res || [], groupBy));
|
||||
};
|
||||
|
||||
const exec = async () => {
|
||||
if (!onSearchSync || !open) return;
|
||||
|
||||
if (triggerSearchOnFocus) {
|
||||
doSearchSync();
|
||||
}
|
||||
|
||||
if (debouncedSearchTerm) {
|
||||
doSearchSync();
|
||||
}
|
||||
};
|
||||
|
||||
void exec();
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [debouncedSearchTerm, groupBy, open, triggerSearchOnFocus]);
|
||||
|
||||
useEffect(() => {
|
||||
/** async search */
|
||||
|
||||
const doSearch = async () => {
|
||||
setIsLoading(true);
|
||||
const res = await onSearch?.(debouncedSearchTerm);
|
||||
setOptions(transToGroupOption(res || [], groupBy));
|
||||
setIsLoading(false);
|
||||
};
|
||||
|
||||
const exec = async () => {
|
||||
if (!onSearch || !open) return;
|
||||
|
||||
if (triggerSearchOnFocus) {
|
||||
await doSearch();
|
||||
}
|
||||
|
||||
if (debouncedSearchTerm) {
|
||||
await doSearch();
|
||||
}
|
||||
};
|
||||
|
||||
void exec();
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [debouncedSearchTerm, groupBy, open, triggerSearchOnFocus]);
|
||||
|
||||
const CreatableItem = () => {
|
||||
if (!creatable) return undefined;
|
||||
if (
|
||||
isOptionsExist(options, [{ value: inputValue, label: inputValue }]) ||
|
||||
selected.find((s) => s.value === inputValue)
|
||||
) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const Item = (
|
||||
<CommandItem
|
||||
value={inputValue}
|
||||
className="cursor-pointer"
|
||||
onMouseDown={(e) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
}}
|
||||
onSelect={(value: string) => {
|
||||
if (selected.length >= maxSelected) {
|
||||
onMaxSelected?.(selected.length);
|
||||
return;
|
||||
}
|
||||
setInputValue('');
|
||||
const newOptions = [...selected, { value, label: value }];
|
||||
setSelected(newOptions);
|
||||
onChange?.(newOptions);
|
||||
}}
|
||||
>
|
||||
{`Create "${inputValue}"`}
|
||||
</CommandItem>
|
||||
);
|
||||
|
||||
// For normal creatable
|
||||
if (!onSearch && inputValue.length > 0) {
|
||||
return Item;
|
||||
}
|
||||
|
||||
// For async search creatable. avoid showing creatable item before loading at first.
|
||||
if (onSearch && debouncedSearchTerm.length > 0 && !isLoading) {
|
||||
return Item;
|
||||
}
|
||||
|
||||
return undefined;
|
||||
};
|
||||
|
||||
const EmptyItem = React.useCallback(() => {
|
||||
if (!emptyIndicator) return undefined;
|
||||
|
||||
// For async search that showing emptyIndicator
|
||||
if (onSearch && !creatable && Object.keys(options).length === 0) {
|
||||
return (
|
||||
<CommandItem value="-" disabled>
|
||||
{emptyIndicator}
|
||||
</CommandItem>
|
||||
);
|
||||
}
|
||||
|
||||
return <CommandEmpty>{emptyIndicator}</CommandEmpty>;
|
||||
}, [creatable, emptyIndicator, onSearch, options]);
|
||||
|
||||
const selectables = React.useMemo<GroupOption>(
|
||||
() => removePickedOption(options, selected),
|
||||
[options, selected],
|
||||
);
|
||||
|
||||
/** Avoid Creatable Selector freezing or lagging when paste a long string. */
|
||||
const commandFilter = React.useCallback(() => {
|
||||
if (commandProps?.filter) {
|
||||
return commandProps.filter;
|
||||
}
|
||||
|
||||
if (creatable) {
|
||||
return (value: string, search: string) => {
|
||||
return value.toLowerCase().includes(search.toLowerCase()) ? 1 : -1;
|
||||
};
|
||||
}
|
||||
// Using default filter in `cmdk`. We don't have to provide it.
|
||||
return undefined;
|
||||
}, [creatable, commandProps?.filter]);
|
||||
|
||||
return (
|
||||
<Command
|
||||
ref={dropdownRef}
|
||||
{...commandProps}
|
||||
onKeyDown={(e) => {
|
||||
handleKeyDown(e);
|
||||
commandProps?.onKeyDown?.(e);
|
||||
}}
|
||||
className={cn(
|
||||
'h-auto overflow-visible bg-transparent',
|
||||
commandProps?.className,
|
||||
)}
|
||||
shouldFilter={
|
||||
commandProps?.shouldFilter !== undefined
|
||||
? commandProps.shouldFilter
|
||||
: !onSearch
|
||||
} // When onSearch is provided, we don't want to filter the options. You can still override it.
|
||||
filter={commandFilter()}
|
||||
>
|
||||
<div
|
||||
className={cn(
|
||||
'min-h-10 rounded-md border border-input text-base ring-offset-background focus-within:ring-2 focus-within:ring-ring focus-within:ring-offset-2 md:text-sm',
|
||||
{
|
||||
'px-3 py-2': selected.length !== 0,
|
||||
'cursor-text': !disabled && selected.length !== 0,
|
||||
},
|
||||
className,
|
||||
)}
|
||||
onClick={() => {
|
||||
if (disabled) return;
|
||||
inputRef?.current?.focus();
|
||||
}}
|
||||
>
|
||||
<div className="relative flex flex-wrap gap-1">
|
||||
{selected.map((option) => {
|
||||
return (
|
||||
<Badge
|
||||
key={option.value}
|
||||
className={cn(
|
||||
'data-[disabled]:bg-muted-foreground data-[disabled]:text-muted data-[disabled]:hover:bg-muted-foreground',
|
||||
'data-[fixed]:bg-muted-foreground data-[fixed]:text-muted data-[fixed]:hover:bg-muted-foreground',
|
||||
badgeClassName,
|
||||
)}
|
||||
data-fixed={option.fixed}
|
||||
data-disabled={disabled || undefined}
|
||||
>
|
||||
{option.label}
|
||||
<button
|
||||
type="button"
|
||||
className={cn(
|
||||
'ml-1 rounded-full outline-none ring-offset-background focus:ring-2 focus:ring-ring focus:ring-offset-2',
|
||||
(disabled || option.fixed) && 'hidden',
|
||||
)}
|
||||
onKeyDown={(e) => {
|
||||
if (e.key === 'Enter') {
|
||||
handleUnselect(option);
|
||||
}
|
||||
}}
|
||||
onMouseDown={(e) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
}}
|
||||
onClick={() => handleUnselect(option)}
|
||||
>
|
||||
<X className="h-3 w-3 text-muted-foreground hover:text-foreground" />
|
||||
</button>
|
||||
</Badge>
|
||||
);
|
||||
})}
|
||||
{/* Avoid having the "Search" Icon */}
|
||||
<CommandPrimitive.Input
|
||||
{...inputProps}
|
||||
ref={inputRef}
|
||||
value={inputValue}
|
||||
disabled={disabled}
|
||||
onValueChange={(value) => {
|
||||
setInputValue(value);
|
||||
inputProps?.onValueChange?.(value);
|
||||
}}
|
||||
onBlur={(event) => {
|
||||
if (!onScrollbar) {
|
||||
setOpen(false);
|
||||
}
|
||||
inputProps?.onBlur?.(event);
|
||||
}}
|
||||
onFocus={(event) => {
|
||||
setOpen(true);
|
||||
inputProps?.onFocus?.(event);
|
||||
}}
|
||||
placeholder={
|
||||
hidePlaceholderWhenSelected && selected.length !== 0
|
||||
? ''
|
||||
: placeholder
|
||||
}
|
||||
className={cn(
|
||||
'flex-1 bg-transparent outline-none placeholder:text-muted-foreground',
|
||||
{
|
||||
'w-full': hidePlaceholderWhenSelected,
|
||||
'px-3 py-2': selected.length === 0,
|
||||
'ml-1': selected.length !== 0,
|
||||
},
|
||||
inputProps?.className,
|
||||
)}
|
||||
/>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => {
|
||||
setSelected(selected.filter((s) => s.fixed));
|
||||
onChange?.(selected.filter((s) => s.fixed));
|
||||
}}
|
||||
className={cn(
|
||||
'absolute right-0 h-6 w-6 p-0',
|
||||
(hideClearAllButton ||
|
||||
disabled ||
|
||||
selected.length < 1 ||
|
||||
selected.filter((s) => s.fixed).length === selected.length) &&
|
||||
'hidden',
|
||||
)}
|
||||
>
|
||||
<X />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div className="relative">
|
||||
{open && (
|
||||
<CommandList
|
||||
className="absolute top-1 z-10 w-full rounded-md border bg-popover text-popover-foreground shadow-md outline-none animate-in"
|
||||
onMouseLeave={() => {
|
||||
setOnScrollbar(false);
|
||||
}}
|
||||
onMouseEnter={() => {
|
||||
setOnScrollbar(true);
|
||||
}}
|
||||
onMouseUp={() => {
|
||||
inputRef?.current?.focus();
|
||||
}}
|
||||
>
|
||||
{isLoading ? (
|
||||
<>{loadingIndicator}</>
|
||||
) : (
|
||||
<>
|
||||
{EmptyItem()}
|
||||
{CreatableItem()}
|
||||
{!selectFirstItem && (
|
||||
<CommandItem value="-" className="hidden" />
|
||||
)}
|
||||
{Object.entries(selectables).map(([key, dropdowns]) => (
|
||||
<CommandGroup
|
||||
key={key}
|
||||
heading={key}
|
||||
className="h-full overflow-auto"
|
||||
>
|
||||
<>
|
||||
{dropdowns.map((option) => {
|
||||
return (
|
||||
<CommandItem
|
||||
key={option.value}
|
||||
value={option.label}
|
||||
disabled={option.disable}
|
||||
onMouseDown={(e) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
}}
|
||||
onSelect={() => {
|
||||
if (selected.length >= maxSelected) {
|
||||
onMaxSelected?.(selected.length);
|
||||
return;
|
||||
}
|
||||
setInputValue('');
|
||||
const newOptions = [...selected, option];
|
||||
setSelected(newOptions);
|
||||
onChange?.(newOptions);
|
||||
}}
|
||||
className={cn(
|
||||
'cursor-pointer',
|
||||
option.disable &&
|
||||
'cursor-default text-muted-foreground',
|
||||
)}
|
||||
>
|
||||
{option.label}
|
||||
</CommandItem>
|
||||
);
|
||||
})}
|
||||
</>
|
||||
</CommandGroup>
|
||||
))}
|
||||
</>
|
||||
)}
|
||||
</CommandList>
|
||||
)}
|
||||
</div>
|
||||
</Command>
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
MultipleSelector.displayName = 'MultipleSelector';
|
||||
export default MultipleSelector;
|
||||
8
src/lib/data/anthropic.json
Normal file
8
src/lib/data/anthropic.json
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
[
|
||||
"claude-3-7-sonnet-20250219:thinking",
|
||||
"claude-3-7-sonnet-20250219",
|
||||
"claude-3-5-sonnet-20241022",
|
||||
"claude-3-5-haiku-20241022",
|
||||
"claude-3-opus-20240229",
|
||||
"claude-3-haiku-20240307"
|
||||
]
|
||||
32
src/lib/data/bedrock.json
Normal file
32
src/lib/data/bedrock.json
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
[
|
||||
"amazon.nova-pro-v1:0",
|
||||
"amazon.nova-pro-latency-optimized-v1:0",
|
||||
"amazon.nova-lite-v1:0",
|
||||
"amazon.nova-micro-v1:0",
|
||||
"anthropic.claude-3-7-sonnet-20250219-v1:0",
|
||||
"anthropic.claude-3-5-sonnet-20241022-v2:0",
|
||||
"anthropic.claude-3-5-haiku-20241022-v1:0",
|
||||
"anthropic.claude-3-5-sonnet-20240620-v1:0",
|
||||
"anthropic.claude-3-opus-20240229-v1:0",
|
||||
"anthropic.claude-3-sonnet-20240229-v1:0",
|
||||
"anthropic.claude-3-haiku-20240307-v1:0",
|
||||
"anthropic.claude-2-1-v1:0",
|
||||
"anthropic.claude-2-0-v1:0",
|
||||
"anthropic.claude-instant-v1:0",
|
||||
"deepseek.r1-v1:0",
|
||||
"meta.llama3-3-70b-instruct-v1:0",
|
||||
"meta.llama3-2-90b-instruct-v1:0",
|
||||
"meta.llama3-2-11b-instruct-v1:0",
|
||||
"meta.llama3-2-3b-instruct-v1:0",
|
||||
"meta.llama3-2-1b-instruct-v1:0",
|
||||
"meta.llama3-1-405b-instruct-v1:0",
|
||||
"meta.llama3-1-70b-instruct-v1:0",
|
||||
"meta.llama3-1-70b-instruct-latency-optimized-v1:0",
|
||||
"meta.llama3-1-8b-instruct-v1:0",
|
||||
"meta.llama3-70b-instruct-v1:0",
|
||||
"meta.llama3-8b-instruct-v1:0",
|
||||
"amazon.titan-text-lite-v1:0",
|
||||
"amazon.titan-text-express-v1:0",
|
||||
"amazon.titan-text-embeddings-v1:0",
|
||||
"amazon.titan-text-embeddings-v2:0"
|
||||
]
|
||||
23
src/lib/data/chutes.json
Normal file
23
src/lib/data/chutes.json
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
[
|
||||
"deepseek-ai/DeepSeek-R1",
|
||||
"deepseek-ai/DeepSeek-V3",
|
||||
"unsloth/Llama-3.3-70B-Instruct",
|
||||
"chutesai/Llama-4-Scout-17B-16E-Instruct",
|
||||
"unsloth/Mistral-Nemo-Instruct-2407",
|
||||
"unsloth/gemma-3-12b-it",
|
||||
"NousResearch/DeepHermes-3-Llama-3-8B-Preview",
|
||||
"unsloth/gemma-3-4b-it",
|
||||
"nvidia/Llama-3_3-Nemotron-Super-49B-v1",
|
||||
"nvidia/Llama-3_1-Nemotron-Ultra-253B-v1",
|
||||
"chutesai/Llama-4-Maverick-17B-128E-Instruct-FP8",
|
||||
"deepseek-ai/DeepSeek-V3-Base",
|
||||
"deepseek-ai/DeepSeek-R1-Zero",
|
||||
"deepseek-ai/DeepSeek-V3-0324",
|
||||
"Qwen/Qwen3-235B-A22B",
|
||||
"Qwen/Qwen3-32B",
|
||||
"Qwen/Qwen3-30B-A3B",
|
||||
"Qwen/Qwen3-14B",
|
||||
"Qwen/Qwen3-8B",
|
||||
"microsoft/MAI-DS-R1-FP8",
|
||||
"tngtech/DeepSeek-R1T-Chimera"
|
||||
]
|
||||
4
src/lib/data/deepseek.json
Normal file
4
src/lib/data/deepseek.json
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
[
|
||||
"deepseek-chat",
|
||||
"deepseek-reasoner"
|
||||
]
|
||||
21
src/lib/data/gemini.json
Normal file
21
src/lib/data/gemini.json
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
[
|
||||
"gemini-2.5-flash-preview-04-17:thinking",
|
||||
"gemini-2.5-flash-preview-04-17",
|
||||
"gemini-2.5-flash-preview-05-20:thinking",
|
||||
"gemini-2.5-flash-preview-05-20",
|
||||
"gemini-2.5-pro-exp-03-25",
|
||||
"gemini-2.5-pro-preview-03-25",
|
||||
"gemini-2.5-pro-preview-05-06",
|
||||
"gemini-2.0-flash-001",
|
||||
"gemini-2.0-flash-lite-preview-02-05",
|
||||
"gemini-2.0-pro-exp-02-05",
|
||||
"gemini-2.0-flash-thinking-exp-01-21",
|
||||
"gemini-2.0-flash-thinking-exp-1219",
|
||||
"gemini-2.0-flash-exp",
|
||||
"gemini-1.5-flash-002",
|
||||
"gemini-1.5-flash-exp-0827",
|
||||
"gemini-1.5-flash-8b-exp-0827",
|
||||
"gemini-1.5-pro-002",
|
||||
"gemini-1.5-pro-exp-0827",
|
||||
"gemini-exp-1206"
|
||||
]
|
||||
96
src/lib/data/glama.json
Normal file
96
src/lib/data/glama.json
Normal file
|
|
@ -0,0 +1,96 @@
|
|||
[
|
||||
"alibaba/qwen-plus",
|
||||
"alibaba/qwen-turbo",
|
||||
"anthropic/claude-3-5-haiku-20241022",
|
||||
"anthropic/claude-3-5-haiku",
|
||||
"anthropic/claude-3-5-sonnet-20240620",
|
||||
"anthropic/claude-3-5-sonnet-20241022",
|
||||
"anthropic/claude-3-5-sonnet",
|
||||
"anthropic/claude-3-7-sonnet-20250219",
|
||||
"anthropic/claude-3-7-sonnet",
|
||||
"anthropic/claude-3-opus-20240229",
|
||||
"anthropic/claude-3-opus",
|
||||
"avian/deepseek-r1",
|
||||
"avian/llama-3.1-405b-instruct",
|
||||
"avian/llama-3.1-70b-instruct",
|
||||
"avian/llama-3.1-8b-instruct",
|
||||
"avian/llama-3.3-70b-instruct",
|
||||
"cloudflare/deepseek-r1-distill-qwen-32b",
|
||||
"cohere/command-r-08-2024",
|
||||
"cohere/command-r-plus-08-2024",
|
||||
"cohere/command-r-plus",
|
||||
"cohere/command-r",
|
||||
"cohere/command-r7b-12-2024",
|
||||
"cohere/command-r7b",
|
||||
"deepinfra/qwq-32b-preview",
|
||||
"deepseek/deepseek-chat-v3",
|
||||
"deepseek/deepseek-chat",
|
||||
"deepseek/deepseek-r1",
|
||||
"fireworks/deepseek-chat-v3",
|
||||
"fireworks/deepseek-chat",
|
||||
"google-ai-studio/gemini-2.0-flash-001",
|
||||
"google-ai-studio/gemini-2.0-flash-lite-preview-02-05",
|
||||
"google-ai-studio/gemini-2.0-flash-lite",
|
||||
"google-ai-studio/gemini-2.0-flash",
|
||||
"google-ai-studio/gemini-2.5-flash-preview-04-17",
|
||||
"google-ai-studio/gemini-2.5-flash",
|
||||
"google-ai-studio/gemini-2.5-pro-exp-03-25",
|
||||
"google-ai-studio/gemini-2.5-pro-preview-05-06",
|
||||
"google-ai-studio/gemini-2.5-pro",
|
||||
"google-vertex/gemini-1.5-flash-001",
|
||||
"google-vertex/gemini-1.5-flash-002",
|
||||
"google-vertex/gemini-1.5-flash",
|
||||
"google-vertex/gemini-1.5-pro-001",
|
||||
"google-vertex/gemini-1.5-pro-002",
|
||||
"google-vertex/gemini-1.5-pro",
|
||||
"google-vertex/gemini-2.0-flash-001",
|
||||
"google-vertex/gemini-2.0-flash-lite-preview-02-05",
|
||||
"google-vertex/gemini-2.0-flash-lite",
|
||||
"google-vertex/gemini-2.0-flash-thinking-exp-01-21",
|
||||
"google-vertex/gemini-2.0-flash-thinking",
|
||||
"google-vertex/gemini-2.0-flash",
|
||||
"google-vertex/gemini-2.5-flash-preview-04-17",
|
||||
"google-vertex/gemini-2.5-flash",
|
||||
"google-vertex/gemini-2.5-pro-preview-05-06",
|
||||
"google-vertex/gemini-2.5-pro",
|
||||
"groq/deepseek-r1-distill-llama-70b",
|
||||
"mistral/ministral-3b-2410",
|
||||
"mistral/ministral-3b",
|
||||
"mistral/ministral-8b-2410",
|
||||
"mistral/ministral-8b",
|
||||
"mistral/mistral-large-2411",
|
||||
"mistral/mistral-large",
|
||||
"openai/gpt-4.1-2025-04-14",
|
||||
"openai/gpt-4.1-mini-2025-04-14",
|
||||
"openai/gpt-4.1-mini",
|
||||
"openai/gpt-4.1-nano-2025-04-14",
|
||||
"openai/gpt-4.1-nano",
|
||||
"openai/gpt-4.1",
|
||||
"openai/gpt-4.5-preview-2025-02-27",
|
||||
"openai/gpt-4.5",
|
||||
"openai/gpt-4o-2024-05-13",
|
||||
"openai/gpt-4o-2024-08-06",
|
||||
"openai/gpt-4o-2024-11-20",
|
||||
"openai/gpt-4o-mini-2024-07-18",
|
||||
"openai/gpt-4o-mini",
|
||||
"openai/gpt-4o",
|
||||
"openai/o1-2024-12-17",
|
||||
"openai/o1-mini-2024-09-12",
|
||||
"openai/o3-2025-04-16",
|
||||
"openai/o3-mini-2025-01-31",
|
||||
"openai/o3-mini-high",
|
||||
"openai/o3-mini",
|
||||
"openai/o3",
|
||||
"openai/o4-mini-2025-04-16",
|
||||
"openai/o4-mini",
|
||||
"perplexity/r1-1776",
|
||||
"perplexity/r1",
|
||||
"xai/grok-2-1212",
|
||||
"xai/grok-2",
|
||||
"xai/grok-3-beta",
|
||||
"xai/grok-3-fast-beta",
|
||||
"xai/grok-3-mini-beta",
|
||||
"xai/grok-3-mini-fast-beta",
|
||||
"xai/grok-beta",
|
||||
"xai/grok"
|
||||
]
|
||||
9
src/lib/data/groq.json
Normal file
9
src/lib/data/groq.json
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
[
|
||||
"llama-3.1-8b-instant",
|
||||
"llama-3.3-70b-versatile",
|
||||
"meta-llama/llama-4-scout-17b-16e-instruct",
|
||||
"meta-llama/llama-4-maverick-17b-128e-instruct",
|
||||
"mistral-saba-24b",
|
||||
"qwen-qwq-32b",
|
||||
"deepseek-r1-distill-llama-70b"
|
||||
]
|
||||
8
src/lib/data/mistral.json
Normal file
8
src/lib/data/mistral.json
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
[
|
||||
"codestral-latest",
|
||||
"mistral-large-latest",
|
||||
"ministral-8b-latest",
|
||||
"ministral-3b-latest",
|
||||
"mistral-small-latest",
|
||||
"pixtral-large-latest"
|
||||
]
|
||||
20
src/lib/data/openai-native.json
Normal file
20
src/lib/data/openai-native.json
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
[
|
||||
"gpt-4.1",
|
||||
"gpt-4.1-mini",
|
||||
"gpt-4.1-nano",
|
||||
"o3",
|
||||
"o3-high",
|
||||
"o3-low",
|
||||
"o4-mini",
|
||||
"o4-mini-high",
|
||||
"o4-mini-low",
|
||||
"o3-mini",
|
||||
"o3-mini-high",
|
||||
"o3-mini-low",
|
||||
"o1",
|
||||
"o1-preview",
|
||||
"o1-mini",
|
||||
"gpt-4.5-preview",
|
||||
"gpt-4o",
|
||||
"gpt-4o-mini"
|
||||
]
|
||||
321
src/lib/data/openrouter-models.json
Normal file
321
src/lib/data/openrouter-models.json
Normal file
|
|
@ -0,0 +1,321 @@
|
|||
[
|
||||
"aetherwiing/mn-starcannon-12b",
|
||||
"agentica-org/deepcoder-14b-preview:free",
|
||||
"ai21/jamba-1.6-large",
|
||||
"ai21/jamba-1.6-mini",
|
||||
"ai21/jamba-instruct",
|
||||
"aion-labs/aion-1.0-mini",
|
||||
"aion-labs/aion-1.0",
|
||||
"aion-labs/aion-rp-llama-3.1-8b",
|
||||
"alfredpros/codellama-7b-instruct-solidity",
|
||||
"all-hands/openhands-lm-32b-v0.1",
|
||||
"allenai/olmo-7b-instruct",
|
||||
"alpindale/goliath-120b",
|
||||
"alpindale/magnum-72b",
|
||||
"amazon/nova-lite-v1",
|
||||
"amazon/nova-micro-v1",
|
||||
"amazon/nova-pro-v1",
|
||||
"anthracite-org/magnum-v2-72b",
|
||||
"anthracite-org/magnum-v4-72b",
|
||||
"anthropic/claude-2:beta",
|
||||
"anthropic/claude-2.0:beta",
|
||||
"anthropic/claude-2.0",
|
||||
"anthropic/claude-2.1:beta",
|
||||
"anthropic/claude-2.1",
|
||||
"anthropic/claude-2",
|
||||
"anthropic/claude-3-haiku:beta",
|
||||
"anthropic/claude-3-haiku",
|
||||
"anthropic/claude-3-opus:beta",
|
||||
"anthropic/claude-3-opus",
|
||||
"anthropic/claude-3-sonnet:beta",
|
||||
"anthropic/claude-3-sonnet",
|
||||
"anthropic/claude-3.5-haiku-20241022:beta",
|
||||
"anthropic/claude-3.5-haiku-20241022",
|
||||
"anthropic/claude-3.5-haiku:beta",
|
||||
"anthropic/claude-3.5-haiku",
|
||||
"anthropic/claude-3.5-sonnet-20240620:beta",
|
||||
"anthropic/claude-3.5-sonnet-20240620",
|
||||
"anthropic/claude-3.5-sonnet:beta",
|
||||
"anthropic/claude-3.5-sonnet",
|
||||
"anthropic/claude-3.7-sonnet:beta",
|
||||
"anthropic/claude-3.7-sonnet:thinking",
|
||||
"anthropic/claude-3.7-sonnet",
|
||||
"arcee-ai/arcee-blitz",
|
||||
"arcee-ai/caller-large",
|
||||
"arcee-ai/coder-large",
|
||||
"arcee-ai/maestro-reasoning",
|
||||
"arcee-ai/spotlight",
|
||||
"arcee-ai/virtuoso-large",
|
||||
"arcee-ai/virtuoso-medium-v2",
|
||||
"arliai/qwq-32b-arliai-rpr-v1:free",
|
||||
"cognitivecomputations/dolphin-mixtral-8x22b",
|
||||
"cognitivecomputations/dolphin3.0-mistral-24b:free",
|
||||
"cognitivecomputations/dolphin3.0-r1-mistral-24b:free",
|
||||
"cohere/command-a",
|
||||
"cohere/command-r-03-2024",
|
||||
"cohere/command-r-08-2024",
|
||||
"cohere/command-r-plus-04-2024",
|
||||
"cohere/command-r-plus-08-2024",
|
||||
"cohere/command-r-plus",
|
||||
"cohere/command-r",
|
||||
"cohere/command-r7b-12-2024",
|
||||
"cohere/command",
|
||||
"deepseek/deepseek-chat-v3-0324:free",
|
||||
"deepseek/deepseek-chat-v3-0324",
|
||||
"deepseek/deepseek-chat:free",
|
||||
"deepseek/deepseek-chat",
|
||||
"deepseek/deepseek-coder",
|
||||
"deepseek/deepseek-prover-v2:free",
|
||||
"deepseek/deepseek-prover-v2",
|
||||
"deepseek/deepseek-r1-distill-llama-70b:free",
|
||||
"deepseek/deepseek-r1-distill-llama-70b",
|
||||
"deepseek/deepseek-r1-distill-llama-8b",
|
||||
"deepseek/deepseek-r1-distill-qwen-1.5b",
|
||||
"deepseek/deepseek-r1-distill-qwen-14b:free",
|
||||
"deepseek/deepseek-r1-distill-qwen-14b",
|
||||
"deepseek/deepseek-r1-distill-qwen-32b:free",
|
||||
"deepseek/deepseek-r1-distill-qwen-32b",
|
||||
"deepseek/deepseek-r1-zero:free",
|
||||
"deepseek/deepseek-r1:free",
|
||||
"deepseek/deepseek-r1",
|
||||
"deepseek/deepseek-v3-base:free",
|
||||
"eleutherai/llemma_7b",
|
||||
"eva-unit-01/eva-llama-3.33-70b",
|
||||
"eva-unit-01/eva-qwen-2.5-32b",
|
||||
"eva-unit-01/eva-qwen-2.5-72b",
|
||||
"featherless/qwerky-72b:free",
|
||||
"google/gemini-2.0-flash-001",
|
||||
"google/gemini-2.0-flash-exp:free",
|
||||
"google/gemini-2.0-flash-lite-001",
|
||||
"google/gemini-2.5-flash-preview-05-20:thinking",
|
||||
"google/gemini-2.5-flash-preview-05-20",
|
||||
"google/gemini-2.5-flash-preview:thinking",
|
||||
"google/gemini-2.5-flash-preview",
|
||||
"google/gemini-2.5-pro-exp-03-25",
|
||||
"google/gemini-2.5-pro-preview",
|
||||
"google/gemini-flash-1.5-8b",
|
||||
"google/gemini-flash-1.5",
|
||||
"google/gemini-pro-1.5",
|
||||
"google/gemma-2-27b-it",
|
||||
"google/gemma-2-9b-it:free",
|
||||
"google/gemma-2-9b-it",
|
||||
"google/gemma-3-12b-it:free",
|
||||
"google/gemma-3-12b-it",
|
||||
"google/gemma-3-1b-it:free",
|
||||
"google/gemma-3-27b-it:free",
|
||||
"google/gemma-3-27b-it",
|
||||
"google/gemma-3-4b-it:free",
|
||||
"google/gemma-3-4b-it",
|
||||
"gryphe/mythomax-l2-13b",
|
||||
"inception/mercury-coder-small-beta",
|
||||
"infermatic/mn-inferor-12b",
|
||||
"inflection/inflection-3-pi",
|
||||
"inflection/inflection-3-productivity",
|
||||
"jondurbin/airoboros-l2-70b",
|
||||
"liquid/lfm-3b",
|
||||
"liquid/lfm-40b",
|
||||
"liquid/lfm-7b",
|
||||
"mancer/weaver",
|
||||
"meta-llama/llama-2-70b-chat",
|
||||
"meta-llama/llama-3-70b-instruct",
|
||||
"meta-llama/llama-3-8b-instruct",
|
||||
"meta-llama/llama-3.1-405b-instruct",
|
||||
"meta-llama/llama-3.1-405b:free",
|
||||
"meta-llama/llama-3.1-405b",
|
||||
"meta-llama/llama-3.1-70b-instruct",
|
||||
"meta-llama/llama-3.1-8b-instruct:free",
|
||||
"meta-llama/llama-3.1-8b-instruct",
|
||||
"meta-llama/llama-3.2-11b-vision-instruct:free",
|
||||
"meta-llama/llama-3.2-11b-vision-instruct",
|
||||
"meta-llama/llama-3.2-1b-instruct:free",
|
||||
"meta-llama/llama-3.2-1b-instruct",
|
||||
"meta-llama/llama-3.2-3b-instruct:free",
|
||||
"meta-llama/llama-3.2-3b-instruct",
|
||||
"meta-llama/llama-3.2-90b-vision-instruct",
|
||||
"meta-llama/llama-3.3-70b-instruct:free",
|
||||
"meta-llama/llama-3.3-70b-instruct",
|
||||
"meta-llama/llama-3.3-8b-instruct:free",
|
||||
"meta-llama/llama-4-maverick:free",
|
||||
"meta-llama/llama-4-maverick",
|
||||
"meta-llama/llama-4-scout:free",
|
||||
"meta-llama/llama-4-scout",
|
||||
"meta-llama/llama-guard-2-8b",
|
||||
"meta-llama/llama-guard-3-8b",
|
||||
"meta-llama/llama-guard-4-12b",
|
||||
"microsoft/mai-ds-r1:free",
|
||||
"microsoft/phi-3-medium-128k-instruct",
|
||||
"microsoft/phi-3-mini-128k-instruct",
|
||||
"microsoft/phi-3.5-mini-128k-instruct",
|
||||
"microsoft/phi-4-multimodal-instruct",
|
||||
"microsoft/phi-4-reasoning-plus:free",
|
||||
"microsoft/phi-4-reasoning-plus",
|
||||
"microsoft/phi-4-reasoning:free",
|
||||
"microsoft/phi-4",
|
||||
"microsoft/wizardlm-2-8x22b",
|
||||
"minimax/minimax-01",
|
||||
"mistralai/codestral-2501",
|
||||
"mistralai/codestral-mamba",
|
||||
"mistralai/ministral-3b",
|
||||
"mistralai/ministral-8b",
|
||||
"mistralai/mistral-7b-instruct-v0.1",
|
||||
"mistralai/mistral-7b-instruct-v0.2",
|
||||
"mistralai/mistral-7b-instruct-v0.3",
|
||||
"mistralai/mistral-7b-instruct:free",
|
||||
"mistralai/mistral-7b-instruct",
|
||||
"mistralai/mistral-large-2407",
|
||||
"mistralai/mistral-large-2411",
|
||||
"mistralai/mistral-large",
|
||||
"mistralai/mistral-medium-3",
|
||||
"mistralai/mistral-medium",
|
||||
"mistralai/mistral-nemo:free",
|
||||
"mistralai/mistral-nemo",
|
||||
"mistralai/mistral-saba",
|
||||
"mistralai/mistral-small-24b-instruct-2501:free",
|
||||
"mistralai/mistral-small-24b-instruct-2501",
|
||||
"mistralai/mistral-small-3.1-24b-instruct:free",
|
||||
"mistralai/mistral-small-3.1-24b-instruct",
|
||||
"mistralai/mistral-small",
|
||||
"mistralai/mistral-tiny",
|
||||
"mistralai/mixtral-8x22b-instruct",
|
||||
"mistralai/mixtral-8x7b-instruct",
|
||||
"mistralai/pixtral-12b",
|
||||
"mistralai/pixtral-large-2411",
|
||||
"moonshotai/kimi-vl-a3b-thinking:free",
|
||||
"moonshotai/moonlight-16b-a3b-instruct:free",
|
||||
"neversleep/llama-3-lumimaid-70b",
|
||||
"neversleep/llama-3-lumimaid-8b:extended",
|
||||
"neversleep/llama-3-lumimaid-8b",
|
||||
"neversleep/llama-3.1-lumimaid-70b",
|
||||
"neversleep/llama-3.1-lumimaid-8b",
|
||||
"neversleep/noromaid-20b",
|
||||
"nothingiisreal/mn-celeste-12b",
|
||||
"nousresearch/deephermes-3-llama-3-8b-preview:free",
|
||||
"nousresearch/deephermes-3-mistral-24b-preview:free",
|
||||
"nousresearch/hermes-2-pro-llama-3-8b",
|
||||
"nousresearch/hermes-3-llama-3.1-405b",
|
||||
"nousresearch/hermes-3-llama-3.1-70b",
|
||||
"nousresearch/nous-hermes-2-mixtral-8x7b-dpo",
|
||||
"nvidia/llama-3.1-nemotron-70b-instruct",
|
||||
"nvidia/llama-3.1-nemotron-ultra-253b-v1:free",
|
||||
"nvidia/llama-3.3-nemotron-super-49b-v1:free",
|
||||
"nvidia/llama-3.3-nemotron-super-49b-v1",
|
||||
"open-r1/olympiccoder-32b:free",
|
||||
"openai/chatgpt-4o-latest",
|
||||
"openai/codex-mini",
|
||||
"openai/gpt-3.5-turbo-0125",
|
||||
"openai/gpt-3.5-turbo-0613",
|
||||
"openai/gpt-3.5-turbo-1106",
|
||||
"openai/gpt-3.5-turbo-16k",
|
||||
"openai/gpt-3.5-turbo-instruct",
|
||||
"openai/gpt-3.5-turbo",
|
||||
"openai/gpt-4-0314",
|
||||
"openai/gpt-4-1106-preview",
|
||||
"openai/gpt-4-32k-0314",
|
||||
"openai/gpt-4-32k",
|
||||
"openai/gpt-4-turbo-preview",
|
||||
"openai/gpt-4-turbo",
|
||||
"openai/gpt-4.1-mini",
|
||||
"openai/gpt-4.1-nano",
|
||||
"openai/gpt-4.1",
|
||||
"openai/gpt-4.5-preview",
|
||||
"openai/gpt-4",
|
||||
"openai/gpt-4o-2024-05-13",
|
||||
"openai/gpt-4o-2024-08-06",
|
||||
"openai/gpt-4o-2024-11-20",
|
||||
"openai/gpt-4o-mini-2024-07-18",
|
||||
"openai/gpt-4o-mini-search-preview",
|
||||
"openai/gpt-4o-mini",
|
||||
"openai/gpt-4o-search-preview",
|
||||
"openai/gpt-4o:extended",
|
||||
"openai/gpt-4o",
|
||||
"openai/o1-mini-2024-09-12",
|
||||
"openai/o1-mini",
|
||||
"openai/o1-preview-2024-09-12",
|
||||
"openai/o1-preview",
|
||||
"openai/o1-pro",
|
||||
"openai/o1",
|
||||
"openai/o3-mini-high",
|
||||
"openai/o3-mini",
|
||||
"openai/o3",
|
||||
"openai/o4-mini-high",
|
||||
"openai/o4-mini",
|
||||
"opengvlab/internvl3-14b:free",
|
||||
"opengvlab/internvl3-2b:free",
|
||||
"openrouter/auto",
|
||||
"perplexity/llama-3.1-sonar-large-128k-online",
|
||||
"perplexity/llama-3.1-sonar-small-128k-online",
|
||||
"perplexity/r1-1776",
|
||||
"perplexity/sonar-deep-research",
|
||||
"perplexity/sonar-pro",
|
||||
"perplexity/sonar-reasoning-pro",
|
||||
"perplexity/sonar-reasoning",
|
||||
"perplexity/sonar",
|
||||
"pygmalionai/mythalion-13b",
|
||||
"qwen/qwen-2-72b-instruct",
|
||||
"qwen/qwen-2.5-72b-instruct:free",
|
||||
"qwen/qwen-2.5-72b-instruct",
|
||||
"qwen/qwen-2.5-7b-instruct:free",
|
||||
"qwen/qwen-2.5-7b-instruct",
|
||||
"qwen/qwen-2.5-coder-32b-instruct:free",
|
||||
"qwen/qwen-2.5-coder-32b-instruct",
|
||||
"qwen/qwen-2.5-vl-7b-instruct:free",
|
||||
"qwen/qwen-2.5-vl-7b-instruct",
|
||||
"qwen/qwen-max",
|
||||
"qwen/qwen-plus",
|
||||
"qwen/qwen-turbo",
|
||||
"qwen/qwen-vl-max",
|
||||
"qwen/qwen-vl-plus",
|
||||
"qwen/qwen2.5-coder-7b-instruct",
|
||||
"qwen/qwen2.5-vl-32b-instruct:free",
|
||||
"qwen/qwen2.5-vl-32b-instruct",
|
||||
"qwen/qwen2.5-vl-3b-instruct:free",
|
||||
"qwen/qwen2.5-vl-72b-instruct:free",
|
||||
"qwen/qwen2.5-vl-72b-instruct",
|
||||
"qwen/qwen3-0.6b-04-28:free",
|
||||
"qwen/qwen3-1.7b:free",
|
||||
"qwen/qwen3-14b:free",
|
||||
"qwen/qwen3-14b",
|
||||
"qwen/qwen3-235b-a22b:free",
|
||||
"qwen/qwen3-235b-a22b",
|
||||
"qwen/qwen3-30b-a3b:free",
|
||||
"qwen/qwen3-30b-a3b",
|
||||
"qwen/qwen3-32b:free",
|
||||
"qwen/qwen3-32b",
|
||||
"qwen/qwen3-4b:free",
|
||||
"qwen/qwen3-8b:free",
|
||||
"qwen/qwen3-8b",
|
||||
"qwen/qwq-32b-preview",
|
||||
"qwen/qwq-32b:free",
|
||||
"qwen/qwq-32b",
|
||||
"raifle/sorcererlm-8x22b",
|
||||
"rekaai/reka-flash-3:free",
|
||||
"sao10k/fimbulvetr-11b-v2",
|
||||
"sao10k/l3-euryale-70b",
|
||||
"sao10k/l3-lunaris-8b",
|
||||
"sao10k/l3.1-euryale-70b",
|
||||
"sao10k/l3.3-euryale-70b",
|
||||
"scb10x/llama3.1-typhoon2-70b-instruct",
|
||||
"scb10x/llama3.1-typhoon2-8b-instruct",
|
||||
"shisa-ai/shisa-v2-llama3.3-70b:free",
|
||||
"sophosympatheia/midnight-rose-70b",
|
||||
"thedrummer/anubis-pro-105b-v1",
|
||||
"thedrummer/rocinante-12b",
|
||||
"thedrummer/skyfall-36b-v2",
|
||||
"thedrummer/unslopnemo-12b",
|
||||
"thudm/glm-4-32b:free",
|
||||
"thudm/glm-4-32b",
|
||||
"thudm/glm-4-9b:free",
|
||||
"thudm/glm-z1-32b:free",
|
||||
"thudm/glm-z1-32b",
|
||||
"thudm/glm-z1-9b:free",
|
||||
"thudm/glm-z1-rumination-32b",
|
||||
"tngtech/deepseek-r1t-chimera:free",
|
||||
"undi95/remm-slerp-l2-13b",
|
||||
"undi95/toppy-m-7b",
|
||||
"x-ai/grok-2-1212",
|
||||
"x-ai/grok-2-vision-1212",
|
||||
"x-ai/grok-3-beta",
|
||||
"x-ai/grok-3-mini-beta",
|
||||
"x-ai/grok-beta",
|
||||
"x-ai/grok-vision-beta"
|
||||
]
|
||||
230
src/lib/data/requesty-models.json
Normal file
230
src/lib/data/requesty-models.json
Normal file
|
|
@ -0,0 +1,230 @@
|
|||
[
|
||||
"alibaba/qwen-plus",
|
||||
"alibaba/qwen-turbo",
|
||||
"anthropic/claude-3-5-haiku-20241022",
|
||||
"anthropic/claude-3-5-haiku-latest",
|
||||
"anthropic/claude-3-5-sonnet-20240620",
|
||||
"anthropic/claude-3-5-sonnet-20241022",
|
||||
"anthropic/claude-3-5-sonnet-latest",
|
||||
"anthropic/claude-3-7-sonnet-20250219",
|
||||
"anthropic/claude-3-7-sonnet-latest",
|
||||
"anthropic/claude-3-haiku-20240307",
|
||||
"anthropic/claude-3-opus-20240229",
|
||||
"anthropic/claude-3-opus-latest",
|
||||
"anthropic/claude-3-sonnet-20240229",
|
||||
"cline/claude-3-5-sonnet:alpha-v2",
|
||||
"cline/claude-3-5-sonnet:alpha",
|
||||
"cline/claude-3-5-sonnet",
|
||||
"cline/claude-3-7-sonnet:1024",
|
||||
"cline/claude-3-7-sonnet:16384",
|
||||
"cline/claude-3-7-sonnet:64000",
|
||||
"cline/claude-3-7-sonnet:8192",
|
||||
"cline/claude-3-7-sonnet:high",
|
||||
"cline/claude-3-7-sonnet:low",
|
||||
"cline/claude-3-7-sonnet:max",
|
||||
"cline/claude-3-7-sonnet:medium",
|
||||
"cline/claude-3-7-sonnet",
|
||||
"cline/deepseek-reasoner:alpha",
|
||||
"cline/o3-mini:high",
|
||||
"cline/o3-mini:low",
|
||||
"cline/o3-mini:medium",
|
||||
"cline/o3-mini",
|
||||
"coding/claude-3-7-sonnet",
|
||||
"coding/gemini-2.5-pro-preview-03-25",
|
||||
"coding/gemini-2.5-pro-preview-05-06",
|
||||
"deepinfra/deepseek-ai/DeepSeek-R1-Distill-Llama-70B",
|
||||
"deepinfra/deepseek-ai/DeepSeek-R1",
|
||||
"deepinfra/deepseek-ai/DeepSeek-V3",
|
||||
"deepinfra/meta-llama/Llama-3.2-11B-Vision-Instruct",
|
||||
"deepinfra/meta-llama/Llama-3.2-90B-Vision-Instruct",
|
||||
"deepinfra/meta-llama/Llama-3.3-70B-Instruct-Turbo",
|
||||
"deepinfra/meta-llama/Llama-3.3-70B-Instruct",
|
||||
"deepinfra/meta-llama/Meta-Llama-3.1-405B-Instruct",
|
||||
"deepinfra/meta-llama/Meta-Llama-3.1-70B-Instruct",
|
||||
"deepinfra/meta-llama/Meta-Llama-3.1-8B-Instruct-Turbo",
|
||||
"deepinfra/microsoft/phi-4",
|
||||
"deepinfra/microsoft/WizardLM-2-8x22B",
|
||||
"deepinfra/nvidia/Llama-3.1-Nemotron-70B-Instruct",
|
||||
"deepinfra/Qwen/Qwen2.5-72B-Instruct",
|
||||
"deepinfra/Qwen/Qwen2.5-Coder-32B-Instruct",
|
||||
"deepinfra/Qwen/Qwen3-235B-A22B",
|
||||
"deepinfra/Qwen/Qwen3-32B",
|
||||
"deepinfra/Qwen/QwQ-32B",
|
||||
"deepseek/deepseek-chat",
|
||||
"deepseek/deepseek-reasoner",
|
||||
"google/gemini-1.5-flash-8b-latest",
|
||||
"google/gemini-1.5-flash-8b",
|
||||
"google/gemini-1.5-flash-latest",
|
||||
"google/gemini-1.5-flash",
|
||||
"google/gemini-1.5-pro-latest",
|
||||
"google/gemini-1.5-pro",
|
||||
"google/gemini-2.0-flash-001",
|
||||
"google/gemini-2.0-flash-exp",
|
||||
"google/gemini-2.0-flash-thinking-exp-01-21",
|
||||
"google/gemini-2.0-pro-exp-02-05",
|
||||
"google/gemini-2.5-flash-preview-04-17",
|
||||
"google/gemini-2.5-pro-exp-03-25",
|
||||
"google/gemini-2.5-pro-preview-03-25",
|
||||
"google/gemini-2.5-pro-preview-05-06",
|
||||
"google/gemma-3-27b-it",
|
||||
"groq/qwen-qwq-32b",
|
||||
"minimaxi/DeepSeek-R1",
|
||||
"minimaxi/MiniMax-Text-01",
|
||||
"mistral/mistral-large-latest",
|
||||
"mistral/mistral-small-latest",
|
||||
"mistral/open-mistral-7b",
|
||||
"nebius/deepseek-ai/DeepSeek-R1-fast",
|
||||
"nebius/deepseek-ai/DeepSeek-R1",
|
||||
"nebius/deepseek-ai/DeepSeek-V3-0324-fast",
|
||||
"nebius/deepseek-ai/DeepSeek-V3-0324",
|
||||
"nebius/deepseek-ai/DeepSeek-V3",
|
||||
"nebius/meta-llama/Llama-3.3-70B-Instruct",
|
||||
"nebius/Qwen/QwQ-32B-fast",
|
||||
"nebius/Qwen/QwQ-32B",
|
||||
"novita/deepseek/deepseek_v3",
|
||||
"novita/deepseek/deepseek-prover-v2-671b",
|
||||
"novita/deepseek/deepseek-r1-distill-llama-70b",
|
||||
"novita/deepseek/deepseek-r1-distill-qwen-14b",
|
||||
"novita/deepseek/deepseek-r1-distill-qwen-32b",
|
||||
"novita/deepseek/deepseek-r1-turbo",
|
||||
"novita/deepseek/deepseek-r1",
|
||||
"novita/deepseek/deepseek-v3-0324",
|
||||
"novita/deepseek/deepseek-v3-turbo",
|
||||
"novita/google/gemma-2-9b-it",
|
||||
"novita/gryphe/mythomax-l2-13b",
|
||||
"novita/jondurbin/airoboros-l2-70b",
|
||||
"novita/meta-llama/llama-3-70b-instruct",
|
||||
"novita/meta-llama/llama-3-8b-instruct",
|
||||
"novita/meta-llama/llama-3.1-70b-instruct",
|
||||
"novita/meta-llama/llama-3.1-8b-instruct-bf16",
|
||||
"novita/meta-llama/llama-3.1-8b-instruct-max",
|
||||
"novita/meta-llama/llama-3.1-8b-instruct",
|
||||
"novita/meta-llama/llama-3.2-11b-vision-instruct",
|
||||
"novita/meta-llama/llama-3.2-1b-instruct",
|
||||
"novita/meta-llama/llama-3.2-3b-instruct",
|
||||
"novita/meta-llama/llama-3.3-70b-instruct",
|
||||
"novita/meta-llama/llama-4-maverick-17b-128e-instruct-fp8",
|
||||
"novita/microsoft/wizardlm-2-8x22b",
|
||||
"novita/mistralai/mistral-7b-instruct",
|
||||
"novita/mistralai/mistral-nemo",
|
||||
"novita/nousresearch/hermes-2-pro-llama-3-8b",
|
||||
"novita/nousresearch/nous-hermes-llama2-13b",
|
||||
"novita/openchat/openchat-7b",
|
||||
"novita/qwen/qwen-2-7b-instruct",
|
||||
"novita/qwen/qwen-2-vl-72b-instruct",
|
||||
"novita/qwen/qwen-2.5-72b-instruct",
|
||||
"novita/qwen/qwen2.5-vl-72b-instruct",
|
||||
"novita/qwen/qwen3-235b-a22b-fp8",
|
||||
"novita/qwen/qwq-32b",
|
||||
"novita/sao10k/l3-70b-euryale-v2.1",
|
||||
"novita/sao10k/l3-8b-lunaris",
|
||||
"novita/Sao10K/L3-8B-Stheno-v3.2",
|
||||
"novita/sao10k/l31-70b-euryale-v2.2",
|
||||
"novita/sophosympatheia/midnight-rose-70b",
|
||||
"novita/teknium/openhermes-2.5-mistral-7b",
|
||||
"openai/chatgpt-4o-latest",
|
||||
"openai/gpt-4.1-2025-04-14",
|
||||
"openai/gpt-4.1-mini-2025-04-14",
|
||||
"openai/gpt-4.1-mini",
|
||||
"openai/gpt-4.1-nano-2025-04-14",
|
||||
"openai/gpt-4.1-nano",
|
||||
"openai/gpt-4.1",
|
||||
"openai/gpt-4.5-preview",
|
||||
"openai/gpt-4o-2024-05-13",
|
||||
"openai/gpt-4o-2024-08-06",
|
||||
"openai/gpt-4o-2024-11-20",
|
||||
"openai/gpt-4o-mini-2024-07-18",
|
||||
"openai/gpt-4o-mini",
|
||||
"openai/gpt-4o",
|
||||
"openai/o1-2024-12-17",
|
||||
"openai/o1-mini-2024-09-12",
|
||||
"openai/o1-mini:high",
|
||||
"openai/o1-mini:low",
|
||||
"openai/o1-mini:medium",
|
||||
"openai/o1-mini",
|
||||
"openai/o1:high",
|
||||
"openai/o1:low",
|
||||
"openai/o1:medium",
|
||||
"openai/o1",
|
||||
"openai/o3-2025-04-16",
|
||||
"openai/o3-mini-2025-01-31",
|
||||
"openai/o3-mini:high",
|
||||
"openai/o3-mini:low",
|
||||
"openai/o3-mini:medium",
|
||||
"openai/o3-mini",
|
||||
"openai/o4-mini-2025-04-16",
|
||||
"openai/o4-mini:high",
|
||||
"openai/o4-mini:low",
|
||||
"openai/o4-mini:medium",
|
||||
"openai/o4-mini",
|
||||
"parasail/meta-llama/Llama-4-Maverick-17B-128E-Instruct-FP8",
|
||||
"parasail/meta-llama/Llama-4-Scout-17B-16E-Instruct",
|
||||
"parasail/parasail-anubis-pro",
|
||||
"parasail/parasail-deepseek-r1",
|
||||
"parasail/parasail-eva-25-72b-v02-fp8",
|
||||
"parasail/parasail-gemma3-27b-it",
|
||||
"parasail/parasail-mistral-7b-instruct-03",
|
||||
"parasail/parasail-mistral-nemo",
|
||||
"parasail/parasail-mythomax-13b",
|
||||
"parasail/parasail-qwen-coder32b-longcontext-128",
|
||||
"parasail/parasail-qwen25-vl-72b-instruct",
|
||||
"parasail/parasail-skyfall-36b-v2-fp8",
|
||||
"parasail/parasail-wayfarer-70b-llama33-fp8",
|
||||
"perplexity/sonar-pro",
|
||||
"perplexity/sonar-reasoning-pro",
|
||||
"perplexity/sonar",
|
||||
"together/deepseek-ai/DeepSeek-R1",
|
||||
"together/deepseek-ai/DeepSeek-V3",
|
||||
"together/deepseek-llm-67b-chat",
|
||||
"together/meta-llama/Llama-2-13b-chat-hf",
|
||||
"together/meta-llama/Llama-2-70b-hf",
|
||||
"together/meta-llama/Llama-2-7b-chat-hf",
|
||||
"together/meta-llama/Llama-3-70b-chat-hf",
|
||||
"together/meta-llama/Llama-3-8b-chat-hf",
|
||||
"together/meta-llama/Llama-3.2-3B-Instruct-Turbo",
|
||||
"together/meta-llama/Llama-3.3-70B-Instruct-Turbo",
|
||||
"together/meta-llama/LlamaGuard-2-8b",
|
||||
"together/meta-llama/Meta-Llama-3-70B-Instruct-Lite",
|
||||
"together/meta-llama/Meta-Llama-3-70B-Instruct-Turbo",
|
||||
"together/meta-llama/Meta-Llama-3-8B-Instruct-Lite",
|
||||
"together/meta-llama/Meta-Llama-3.1-405B-Instruct-Turbo",
|
||||
"together/meta-llama/Meta-Llama-3.1-70B-Instruct-Turbo",
|
||||
"together/meta-llama/Meta-Llama-3.1-8B-Instruct-Turbo",
|
||||
"together/meta-llama/Meta-Llama-Guard-3-8B",
|
||||
"together/NousResearch/Nous-Hermes-2-Mixtral-8x7B-DPO",
|
||||
"together/nvidia/Llama-3.1-Nemotron-70B-Instruct-HF",
|
||||
"together/Qwen/Qwen2-72B-Instruct",
|
||||
"together/Qwen/Qwen2.5-72B-Instruct-Turbo",
|
||||
"together/Qwen/Qwen2.5-7B-Instruct-Turbo",
|
||||
"together/Qwen/Qwen2.5-Coder-32B-Instruct",
|
||||
"together/Qwen/QwQ-32B-Preview",
|
||||
"together/upstage/SOLAR-10.7B-Instruct-v1.0",
|
||||
"vertex/anthropic/claude-3-5-sonnet-20241022",
|
||||
"vertex/anthropic/claude-3-5-sonnet-20241022@europe-west1",
|
||||
"vertex/anthropic/claude-3-5-sonnet-20241022@us-east5",
|
||||
"vertex/anthropic/claude-3-5-sonnet-latest",
|
||||
"vertex/anthropic/claude-3-5-sonnet-latest@europe-west1",
|
||||
"vertex/anthropic/claude-3-5-sonnet-latest@us-east5",
|
||||
"vertex/anthropic/claude-3-5-sonnet",
|
||||
"vertex/anthropic/claude-3-5-sonnet@europe-west1",
|
||||
"vertex/anthropic/claude-3-5-sonnet@us-east5",
|
||||
"vertex/anthropic/claude-3-7-sonnet-20250219",
|
||||
"vertex/anthropic/claude-3-7-sonnet-20250219@europe-west1",
|
||||
"vertex/anthropic/claude-3-7-sonnet-20250219@us-east5",
|
||||
"vertex/anthropic/claude-3-7-sonnet-latest",
|
||||
"vertex/anthropic/claude-3-7-sonnet-latest@europe-west1",
|
||||
"vertex/anthropic/claude-3-7-sonnet-latest@us-east5",
|
||||
"vertex/anthropic/claude-3-7-sonnet",
|
||||
"vertex/anthropic/claude-3-7-sonnet@europe-west1",
|
||||
"vertex/anthropic/claude-3-7-sonnet@us-east5",
|
||||
"xai/grok-2-1212",
|
||||
"xai/grok-2-latest",
|
||||
"xai/grok-3-beta",
|
||||
"xai/grok-3-fast-beta",
|
||||
"xai/grok-3-mini-beta:high",
|
||||
"xai/grok-3-mini-beta:low",
|
||||
"xai/grok-3-mini-beta",
|
||||
"xai/grok-3-mini-fast-beta:high",
|
||||
"xai/grok-3-mini-fast-beta:low",
|
||||
"xai/grok-3-mini-fast-beta"
|
||||
]
|
||||
41
src/lib/data/unbound.json
Normal file
41
src/lib/data/unbound.json
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
[
|
||||
"anthropic/claude-3-5-haiku-20241022",
|
||||
"anthropic/claude-3-5-sonnet-20241022",
|
||||
"anthropic/claude-3-7-sonnet-20250219",
|
||||
"anthropic/claude-3-haiku-20240307",
|
||||
"anthropic/claude-3-opus-20240229",
|
||||
"anthropic/claude-3-sonnet-20240229",
|
||||
"bedrock/us.anthropic.claude-3-5-sonnet-20240620-v1:0",
|
||||
"bedrock/us.anthropic.claude-3-7-sonnet-20250219-v1:0",
|
||||
"dashscope/qwen2.5-7b-instruct",
|
||||
"dashscope/qwen2.5-vl-72b-instruct",
|
||||
"google/gemini-1.5-flash",
|
||||
"google/gemini-2.0-flash",
|
||||
"google/gemini-2.0-flash-lite",
|
||||
"google/gemini-2.0-pro-exp-02-05",
|
||||
"google/gemini-2.5-flash-preview-04-17",
|
||||
"google/gemini-2.5-pro-exp-03-25",
|
||||
"google/gemini-2.5-pro-preview-05-06",
|
||||
"meta/meta-llama-3.3-70b-instruct",
|
||||
"mistral-ai/codestral-latest",
|
||||
"openai/gpt-3.5-turbo",
|
||||
"openai/gpt-4.1",
|
||||
"openai/gpt-4.1-mini",
|
||||
"openai/gpt-4.1-nano",
|
||||
"openai/gpt-4.5-preview",
|
||||
"openai/gpt-4o",
|
||||
"openai/gpt-4o-mini",
|
||||
"openai/o1-mini",
|
||||
"openai/o1-preview",
|
||||
"openai/o3",
|
||||
"openai/o3-mini",
|
||||
"openai/o4-mini",
|
||||
"vertex-ai/custom.deepseek-distill-llama-3-8b-instruct",
|
||||
"vertex-ai/deepseek-distill-llama-3-8b-instruct",
|
||||
"x-ai/grok-2-latest",
|
||||
"x-ai/grok-3-beta",
|
||||
"x-ai/grok-3-fast-beta",
|
||||
"x-ai/grok-3-mini-beta",
|
||||
"x-ai/grok-3-mini-fast-beta",
|
||||
"x-ai/grok-beta"
|
||||
]
|
||||
22
src/lib/data/vertex.json
Normal file
22
src/lib/data/vertex.json
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
[
|
||||
"gemini-2.5-flash-preview-05-20:thinking",
|
||||
"gemini-2.5-flash-preview-05-20",
|
||||
"gemini-2.5-flash-preview-04-17:thinking",
|
||||
"gemini-2.5-flash-preview-04-17",
|
||||
"gemini-2.5-pro-preview-03-25",
|
||||
"gemini-2.5-pro-preview-05-06",
|
||||
"gemini-2.5-pro-exp-03-25",
|
||||
"gemini-2.0-pro-exp-02-05",
|
||||
"gemini-2.0-flash-001",
|
||||
"gemini-2.0-flash-lite-001",
|
||||
"gemini-2.0-flash-thinking-exp-01-21",
|
||||
"gemini-1.5-flash-002",
|
||||
"gemini-1.5-pro-002",
|
||||
"claude-3-7-sonnet@20250219:thinking",
|
||||
"claude-3-7-sonnet@20250219",
|
||||
"claude-3-5-sonnet-v2@20241022",
|
||||
"claude-3-5-sonnet@20240620",
|
||||
"claude-3-5-haiku@20241022",
|
||||
"claude-3-opus@20240229",
|
||||
"claude-3-haiku@20240307"
|
||||
]
|
||||
14
src/lib/data/xai.json
Normal file
14
src/lib/data/xai.json
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
[
|
||||
"grok-3-beta",
|
||||
"grok-3-fast-beta",
|
||||
"grok-3-mini-beta",
|
||||
"grok-3-mini-fast-beta",
|
||||
"grok-2-latest",
|
||||
"grok-2",
|
||||
"grok-2-1212",
|
||||
"grok-2-vision-latest",
|
||||
"grok-2-vision",
|
||||
"grok-2-vision-1212",
|
||||
"grok-vision-beta",
|
||||
"grok-beta"
|
||||
]
|
||||
Loading…
Add table
Reference in a new issue