feat(askai): Add providers table to docs (#2684)
Co-authored-by: Dylan Tientcheu <dylan.tientcheu@algolia.com>
This commit is contained in:
parent
175a7d6bf4
commit
942c1ca353
6 changed files with 1769 additions and 14 deletions
|
|
@ -2,17 +2,8 @@
|
|||
title: Bring Your Own LLM
|
||||
---
|
||||
|
||||
AskAI currently supports a Bring Your Own LLM (BYOLLM) model selection, allowing you to connect your preferred provider. Supported models include:
|
||||
import { ProvidersTable } from '../../src/components/ProvidersTable'
|
||||
|
||||
| Provider | Models |
|
||||
| :---- | :---- |
|
||||
| **Anthropic** | Claude 4 Opus, Claude 4 Sonnet, Claude 3.7 Sonnet, Claude 3.5 Sonnet, Claude 3.5 Sonnet, Claude 3.5 Haiku, Claude 3 Opus, Claude 3 Sonnet, Claude 3 Haiku |
|
||||
| **Cerebras** | Llama 3.1 8B, Llama 3.3 70B |
|
||||
| **DeepSeek** | DeepSeek Chat |
|
||||
| **Google Generative AI** | Gemini 2.5 Pro Preview, Gemini 2.5 Flash Preview, Gemini 2.5 Pro Experimental, Gemini 2.0 Flash, Gemini 1.5 Pro, Gemini 1.5 Pro Latest, Gemini 1.5 Flash, Gemini 1.5 Flash Latest, Gemini 1.5 Flash 8B, Gemini 1.5 Flash 8B Latest |
|
||||
| **Groq** | Llama 4 Scout 17B, DeepSeek R1 Distill, Llama 70B, Llama 3.3 70B Versatile, Llama 3.1 8B, Instant, Mistral Saba 24B, Qwen QWQ 32B, Mixtral 8x7B, Gemma 2 9B |
|
||||
| **Mistral AI** | Pixtral Large, Mistral Large, Mistral Small, Pixtral 12B |
|
||||
| **OpenAI** | GPT-4.1, GPT-4.1 Mini, GPT-4.1 Nano, GPT-4o, GPT-4o Mini, GPT-4 Turbo, GPT-4, GPT-3.5 Turbo, O1, O3 Mini, O3 O4 Mini |
|
||||
| **xAI Grok** | Grok 3, Grok 3 Fast, Grok 3 Mini, Grok 3 Mini Fast, Grok 2 1212, Grok Beta |
|
||||
AskAI currently supports a Bring Your Own LLM (BYOLLM) model selection, allowing you to connect your preferred provider.
|
||||
|
||||
You provide your own API key, giving you full control over usage, costs, and LLM behavior.
|
||||
<ProvidersTable />
|
||||
|
|
|
|||
|
|
@ -33,6 +33,7 @@
|
|||
"postcss-import": "16.1.1",
|
||||
"postcss-preset-env": "10.2.4",
|
||||
"prism-react-renderer": "2.4.1",
|
||||
"radix-ui": "^1.4.2",
|
||||
"react": "^18.0.0",
|
||||
"react-dom": "^18.0.0",
|
||||
"react-google-recaptcha": "^3.1.0",
|
||||
|
|
|
|||
208
packages/website/src/components/ProvidersTable.js
Normal file
208
packages/website/src/components/ProvidersTable.js
Normal file
|
|
@ -0,0 +1,208 @@
|
|||
import { DropdownMenu } from 'radix-ui';
|
||||
import React from 'react';
|
||||
|
||||
import { ChevronDown, ChevronUp } from './icons';
|
||||
|
||||
const ASKAI_URL = 'https://askai.algolia.com/api';
|
||||
|
||||
async function fetchProviders({ search, filter, sorts } = {}) {
|
||||
const params = new URLSearchParams();
|
||||
|
||||
if (search !== '') {
|
||||
params.set('search', search);
|
||||
}
|
||||
|
||||
if (filter !== null) {
|
||||
params.set('filter', filter.name);
|
||||
}
|
||||
|
||||
if (sorts) {
|
||||
const sortList = [];
|
||||
Object.entries(sorts).forEach(([key, val]) => {
|
||||
if (val) {
|
||||
sortList.push(`${val === 'desc' ? '-' : ''}${key}`);
|
||||
}
|
||||
});
|
||||
|
||||
params.set('sort', sortList.join(','));
|
||||
}
|
||||
|
||||
const res = await fetch(`${ASKAI_URL}/providers?${params.toString()}`);
|
||||
const data = await res.json();
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
function formatProvidersFilters(providers) {
|
||||
return Object.values(providers).map((provider) => ({
|
||||
name: provider.name,
|
||||
displayName: provider.displayName,
|
||||
}));
|
||||
}
|
||||
|
||||
function FiltersMenu({ providers, selectedProvider, onSelect }) {
|
||||
if (!providers) return null;
|
||||
|
||||
return (
|
||||
<DropdownMenu.Root>
|
||||
<DropdownMenu.Trigger asChild={true}>
|
||||
<button
|
||||
type="button"
|
||||
className="bg-background px-3 py-1.5 text-foreground ring-1 rounded cursor-pointer inline-flex items-center space-x-1 order-1 md:order-2"
|
||||
>
|
||||
<span>{selectedProvider?.displayName ?? 'Filter providers'}</span>
|
||||
<ChevronDown />
|
||||
</button>
|
||||
</DropdownMenu.Trigger>
|
||||
<DropdownMenu.Portal>
|
||||
<DropdownMenu.Content
|
||||
sideOffset={5}
|
||||
align="start"
|
||||
className="bg-background text-popover-foreground py-2 shadow"
|
||||
>
|
||||
<DropdownMenu.Item
|
||||
className="px-6 py-2 cursor-pointer hover:bg-gray-100 dark:hover:bg-gray-900 transition-colors"
|
||||
onClick={() => onSelect(null)}
|
||||
>
|
||||
View all
|
||||
</DropdownMenu.Item>
|
||||
{providers.map((provider) => (
|
||||
<DropdownMenu.Item
|
||||
className="px-6 py-2 cursor-pointer hover:bg-gray-100 dark:hover:bg-gray-900 transition-colors"
|
||||
key={provider.name}
|
||||
onClick={() => onSelect(provider)}
|
||||
>
|
||||
{provider.displayName}
|
||||
</DropdownMenu.Item>
|
||||
))}
|
||||
</DropdownMenu.Content>
|
||||
</DropdownMenu.Portal>
|
||||
</DropdownMenu.Root>
|
||||
);
|
||||
}
|
||||
|
||||
function SortIndicator({ sort }) {
|
||||
if (sort === 'asc') {
|
||||
return <ChevronUp className="ml-2" />;
|
||||
}
|
||||
|
||||
if (sort === 'desc') {
|
||||
return <ChevronDown className="ml-2" />;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
export function ProvidersTable() {
|
||||
const [providers, setProviders] = React.useState({});
|
||||
const [providersFilters, setProvidersFilters] = React.useState(null);
|
||||
const [query, setQuery] = React.useState('');
|
||||
const [debouncedQuery, setDebouncedQuery] = React.useState('');
|
||||
const [filter, setFilter] = React.useState(null);
|
||||
const [sorts, setSorts] = React.useState({});
|
||||
|
||||
const handleFilter = (provider) => {
|
||||
setFilter(provider);
|
||||
};
|
||||
|
||||
const handleSorting = React.useCallback(
|
||||
(sortKey) => {
|
||||
const newSorts = { ...sorts };
|
||||
|
||||
if (newSorts[sortKey]) {
|
||||
if (newSorts[sortKey] === 'asc') {
|
||||
newSorts[sortKey] = 'desc';
|
||||
} else {
|
||||
newSorts[sortKey] = null;
|
||||
}
|
||||
} else {
|
||||
newSorts[sortKey] = 'asc';
|
||||
}
|
||||
|
||||
setSorts(newSorts);
|
||||
},
|
||||
[sorts],
|
||||
);
|
||||
|
||||
React.useEffect(() => {
|
||||
const handler = setTimeout(() => {
|
||||
setDebouncedQuery(query);
|
||||
}, 400);
|
||||
|
||||
return () => {
|
||||
clearTimeout(handler);
|
||||
};
|
||||
}, [query]);
|
||||
|
||||
React.useEffect(() => {
|
||||
async function getProviders(q) {
|
||||
const data = await fetchProviders({
|
||||
search: q,
|
||||
filter,
|
||||
sorts,
|
||||
});
|
||||
|
||||
setProviders(data);
|
||||
|
||||
if (!providersFilters) {
|
||||
setProvidersFilters(formatProvidersFilters(data));
|
||||
}
|
||||
}
|
||||
|
||||
getProviders(debouncedQuery);
|
||||
}, [debouncedQuery, filter, providersFilters, sorts]);
|
||||
|
||||
const rows = React.useMemo(
|
||||
() =>
|
||||
Object.values(providers).map((provider) =>
|
||||
provider.availableModels.map((model) => (
|
||||
<tr key={model.id}>
|
||||
<td>{provider.displayName}</td>
|
||||
<td>{model.displayName}</td>
|
||||
<td>{provider.name}</td>
|
||||
<td>{model.name}</td>
|
||||
</tr>
|
||||
)),
|
||||
),
|
||||
[providers],
|
||||
);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div className="flex items-start md:items-center mb-4 mt-8 md:space-x-4 flex-col md:flex-row">
|
||||
<input
|
||||
type="text"
|
||||
name="providers"
|
||||
placeholder="Search providers and models"
|
||||
className="border rounded px-3 py-1.5 w-full md:w-1/2 placeholder-foreground order-2 md:order-1 mt-4 md:mt-0"
|
||||
onChange={(e) => setQuery(e.target.value)}
|
||||
/>
|
||||
<FiltersMenu selectedProvider={filter} providers={providersFilters} onSelect={handleFilter} />
|
||||
</div>
|
||||
|
||||
<div className="w-full overflow-x-auto">
|
||||
<table className="providers-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th className="cursor-pointer" onClick={() => handleSorting('provider')}>
|
||||
<span className="inline-flex items-center">
|
||||
Provider
|
||||
<SortIndicator sort={sorts.provider} />
|
||||
</span>
|
||||
</th>
|
||||
<th className="cursor-pointer w-1/4" onClick={() => handleSorting('model')}>
|
||||
<span className="inline-flex items-center">
|
||||
Model
|
||||
<SortIndicator sort={sorts.model} />
|
||||
</span>
|
||||
</th>
|
||||
<th>Provider ID</th>
|
||||
<th>Model ID</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>{rows.map((row) => row)}</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
27
packages/website/src/components/icons.js
Normal file
27
packages/website/src/components/icons.js
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
import React from 'react';
|
||||
|
||||
import { cn } from '../lib/utils';
|
||||
|
||||
export function ChevronDown({ className }) {
|
||||
return (
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" className={cn('size-4', className)}>
|
||||
<path
|
||||
fillRule="evenodd"
|
||||
d="M12.53 16.28a.75.75 0 0 1-1.06 0l-7.5-7.5a.75.75 0 0 1 1.06-1.06L12 14.69l6.97-6.97a.75.75 0 1 1 1.06 1.06l-7.5 7.5Z"
|
||||
clipRule="evenodd"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
export function ChevronUp({ className }) {
|
||||
return (
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" className={cn('size-4', className)}>
|
||||
<path
|
||||
fillRule="evenodd"
|
||||
d="M11.47 7.72a.75.75 0 0 1 1.06 0l7.5 7.5a.75.75 0 1 1-1.06 1.06L12 9.31l-6.97 6.97a.75.75 0 0 1-1.06-1.06l7.5-7.5Z"
|
||||
clipRule="evenodd"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
|
@ -11,6 +11,7 @@
|
|||
--ifm-footer-background-color: var(--grey-100);
|
||||
--ifm-menu-color-background-active: var(--ifm-color-emphasis-200);
|
||||
--ifm-announcement-bar-background-color: #21243d;
|
||||
--background: var(--ifm-background-color);
|
||||
--foreground: oklch(0.145 0 0);
|
||||
--card: oklch(1 0 0);
|
||||
--card-foreground: oklch(0.145 0 0);
|
||||
|
|
@ -122,6 +123,7 @@ html[data-theme='dark'] {
|
|||
--ifm-navbar-background-color: #21243d;
|
||||
--ifm-menu-color-background-active: #21243d;
|
||||
--ifm-announcement-bar-background-color: var(--ifm-color-primary);
|
||||
--background: var(--ifm-background-color);
|
||||
--foreground: oklch(0.985 0 0);
|
||||
--card: oklch(0.145 0 0);
|
||||
--card-foreground: oklch(0.985 0 0);
|
||||
|
|
@ -745,3 +747,17 @@ html[data-theme='dark'] .shimmer-effect {
|
|||
box-shadow: 0 2px 16px #60a5fa88;
|
||||
}
|
||||
}
|
||||
|
||||
.providers-table {
|
||||
width: 100%;
|
||||
display: table;
|
||||
position: relative;
|
||||
border-collapse: collapse;
|
||||
}
|
||||
|
||||
.providers-table th {
|
||||
position: sticky;
|
||||
top: 0;
|
||||
text-align: left;
|
||||
background-color: var(--ifm-background-color);
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue