1
0
Fork 0

feat(askai): Add providers table to docs (#2684)

Co-authored-by: Dylan Tientcheu <dylan.tientcheu@algolia.com>
This commit is contained in:
Paul Jankowski 2025-07-24 15:48:24 -04:00 committed by GitHub
parent 175a7d6bf4
commit 942c1ca353
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 1769 additions and 14 deletions

View file

@ -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 />

View file

@ -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",

View 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>
);
}

View 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>
);
}

View file

@ -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);
}

1516
yarn.lock

File diff suppressed because it is too large Load diff