diff --git a/tools/ui/src/lib/components/app/models/ModelsManager/ModelsManager.svelte b/tools/ui/src/lib/components/app/models/ModelsManager/ModelsManager.svelte
index 34dd455270..93c5e29097 100644
--- a/tools/ui/src/lib/components/app/models/ModelsManager/ModelsManager.svelte
+++ b/tools/ui/src/lib/components/app/models/ModelsManager/ModelsManager.svelte
@@ -148,7 +148,11 @@
ordered.push({
backendId: null,
isLocal: false,
- items: leading(chatOnly),
+ // one repo, one row per provider that serves it
+ items: groupModelQuants(
+ chatOnly.flatMap((entry) => entry.quants),
+ true
+ ),
key: 'oai-compat',
kind: 'compat',
label: 'OAI-compat'
diff --git a/tools/ui/src/lib/components/app/models/ModelsManager/ModelsManagerModelsTable.svelte b/tools/ui/src/lib/components/app/models/ModelsManager/ModelsManagerModelsTable.svelte
index 20c6ea86a6..f0177b52cc 100644
--- a/tools/ui/src/lib/components/app/models/ModelsManager/ModelsManagerModelsTable.svelte
+++ b/tools/ui/src/lib/components/app/models/ModelsManager/ModelsManagerModelsTable.svelte
@@ -229,10 +229,13 @@
{#snippet repoRow(entry: ModelQuantGroup, indent = 0, compact = false)}
{@const isExpanded = !collapsedQuants.has(entry.key)}
+ {@const providerCount = new Set(entry.quants.map((option) => option.backendId ?? '')).size}
{@const groupLabel =
- entry.kind === 'variants'
- ? `${entry.quants.length} variants`
- : `${entry.quants.length} quants available`}
+ entry.kind === 'providers'
+ ? `${providerCount} provider${providerCount === 1 ? '' : 's'}`
+ : entry.kind === 'variants'
+ ? `${entry.quants.length} variants`
+ : `${entry.quants.length} quants available`}
{@const anyLoaded = entry.quants.some(isLoadedOption)}
@@ -295,7 +298,7 @@
{/snippet}
-{#snippet quantRow(option: ModelOption, indent = 0, compact = false)}
+{#snippet quantRow(option: ModelOption, indent = 0, compact = false, showProvider = false)}
{@const favorite = isFavorite(option)}
{@const canLoad = getBackendCapabilities(getBackend(option.backendId)).loadUnload}
{@const isHidden = modelsStore.isHidden(option.id)}
@@ -316,7 +319,9 @@
tabindex="0"
>
- {quant}
+
+ {showProvider ? (getBackend(option.backendId)?.name ?? quant) : quant}
+
{option.model}
@@ -347,7 +352,7 @@
{#if !collapsedQuants.has(entry.key)}
{#each entry.quants as quant (quant.id)}
- {@render quantRow(quant, indent + 24, compact)}
+ {@render quantRow(quant, indent + 24, compact, entry.kind === 'providers')}
{/each}
{/if}
{:else}
diff --git a/tools/ui/src/lib/components/app/models/ModelsManager/utils.ts b/tools/ui/src/lib/components/app/models/ModelsManager/utils.ts
index 0ef7ead0cf..84baa09ab3 100644
--- a/tools/ui/src/lib/components/app/models/ModelsManager/utils.ts
+++ b/tools/ui/src/lib/components/app/models/ModelsManager/utils.ts
@@ -41,7 +41,7 @@ export type ModelOverrideMap = Record;
export type { ModelLoadProgress };
/** What a group folds, which decides its label. */
-export type ModelGroupKind = 'quants' | 'variants';
+export type ModelGroupKind = 'providers' | 'quants' | 'variants';
/** One repo of the table, with the rows it ships as. */
export interface ModelQuantGroup {
@@ -181,14 +181,15 @@ export function modelRepoKey(model: string): string {
}
/** Fold the quants of one repo into a single entry, so the table shows one row per model. */
-export function groupModelQuants(models: ModelOption[]): ModelQuantGroup[] {
+export function groupModelQuants(models: ModelOption[], mergeProviders = false): ModelQuantGroup[] {
const groups = new SvelteMap();
for (const option of models) {
const repo = modelRepoKey(option.model);
// groups stay within one backend, so the same repo served by two providers
- // is not read as two quants of one model
- const key = `${option.backendId ?? ''}::${repo}`;
+ // is not read as two quants of one model. The OAI-compat block asks for the
+ // opposite: one repo, one row per provider that serves it.
+ const key = mergeProviders ? repo : `${option.backendId ?? ''}::${repo}`;
const group = groups.get(key);
if (group) {
@@ -204,8 +205,13 @@ export function groupModelQuants(models: ModelOption[]): ModelQuantGroup[] {
const kind = groupKind(group.quants);
const modelIds = group.quants.map((option) => option.model);
- // the very same id twice is not a quant set; keep those rows apart
- if (modelIds.length > 1 && modelIds.every((model) => model === modelIds[0])) {
+ // the very same id twice is not a quant set; keep those rows apart, unless
+ // the group exists to list the providers that serve it
+ if (
+ kind !== 'providers' &&
+ modelIds.length > 1 &&
+ modelIds.every((model) => model === modelIds[0])
+ ) {
return group.quants.map((option) => ({
...group,
base: option,
@@ -219,8 +225,12 @@ export function groupModelQuants(models: ModelOption[]): ModelQuantGroup[] {
});
}
-/** Quants share a quantization suffix; anything else is a variant. */
+/** What a group folds: providers, quants, or variants of one repo. */
function groupKind(quants: ModelOption[]): ModelGroupKind {
+ const backends = new Set(quants.map((option) => option.backendId ?? ''));
+
+ if (backends.size > 1) return 'providers';
+
const isQuant = quants.every(
(option) => (option.parsedId ?? ModelsService.parseModelId(option.model)).quantization
);