Skip to content

Commit 49e3f8c

Browse files
authored
fix(models) Discord model picker doesn't list all models (#85138)
* Add pagination to the discord model picker * Ensure current model is shown as selected in the picker when its first loaded
1 parent 170f72d commit 49e3f8c

5 files changed

Lines changed: 181 additions & 7 deletions

File tree

extensions/discord/src/monitor/model-picker.state.ts

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ const PICKER_ACTIONS = [
2828
"reset",
2929
"cancel",
3030
"recents",
31+
"nav",
3132
] as const;
3233
const PICKER_VIEWS = ["providers", "models", "recents"] as const;
3334

@@ -362,3 +363,27 @@ export function getDiscordModelPickerModelPage(params: {
362363
provider,
363364
};
364365
}
366+
367+
export function resolveDiscordModelPickerPageForModel(params: {
368+
data: ModelsProviderData;
369+
provider: string;
370+
model: string;
371+
pageSize?: number;
372+
}): number {
373+
const provider = normalizeProviderId(params.provider);
374+
const modelSet = params.data.byProvider.get(provider);
375+
if (!modelSet) {
376+
return 1;
377+
}
378+
const sorted = [...modelSet].toSorted();
379+
const index = sorted.indexOf(params.model);
380+
if (index < 0) {
381+
return 1;
382+
}
383+
const pageSize = clampPageSize(
384+
params.pageSize,
385+
DISCORD_MODEL_PICKER_MODEL_PAGE_SIZE,
386+
DISCORD_MODEL_PICKER_MODEL_PAGE_SIZE,
387+
);
388+
return Math.floor(index / pageSize) + 1;
389+
}

extensions/discord/src/monitor/model-picker.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ export {
1414
loadDiscordModelPickerData,
1515
parseDiscordModelPickerCustomId,
1616
parseDiscordModelPickerData,
17+
resolveDiscordModelPickerPageForModel,
1718
} from "./model-picker.state.js";
1819
export type {
1920
DiscordModelPickerAction,

extensions/discord/src/monitor/model-picker.view.ts

Lines changed: 102 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ import {
2929
} from "./model-picker.state.js";
3030

3131
const DISCORD_PROVIDER_BUTTON_LABEL_MAX_CHARS = 18;
32+
const DISCORD_MODEL_PICKER_PAGE_INDICATOR_CUSTOM_ID = "mdlpk:nav-indicator";
3233

3334
type DiscordModelPickerButtonOptions = {
3435
label: string;
@@ -292,6 +293,63 @@ function buildProviderRows(params: {
292293
return rows;
293294
}
294295

296+
function buildPaginationRow(params: {
297+
command: DiscordModelPickerCommandContext;
298+
userId: string;
299+
view: "providers" | "models";
300+
page: number;
301+
totalPages: number;
302+
hasPrev: boolean;
303+
hasNext: boolean;
304+
provider?: string;
305+
runtime?: string;
306+
providerPage?: number;
307+
modelIndex?: number;
308+
}): Row<Button> | null {
309+
if (params.totalPages <= 1) {
310+
return null;
311+
}
312+
const prevButton = createModelPickerButton({
313+
label: "◀ Prev",
314+
style: ButtonStyle.Secondary,
315+
disabled: !params.hasPrev,
316+
customId: buildDiscordModelPickerCustomId({
317+
command: params.command,
318+
action: "nav",
319+
view: params.view,
320+
provider: params.provider,
321+
runtime: params.runtime,
322+
page: Math.max(1, params.page - 1),
323+
providerPage: params.providerPage,
324+
modelIndex: params.modelIndex,
325+
userId: params.userId,
326+
}),
327+
});
328+
const indicatorButton = createModelPickerButton({
329+
label: `Page ${params.page}/${params.totalPages}`,
330+
style: ButtonStyle.Secondary,
331+
disabled: true,
332+
customId: DISCORD_MODEL_PICKER_PAGE_INDICATOR_CUSTOM_ID,
333+
});
334+
const nextButton = createModelPickerButton({
335+
label: "Next ▶",
336+
style: ButtonStyle.Secondary,
337+
disabled: !params.hasNext,
338+
customId: buildDiscordModelPickerCustomId({
339+
command: params.command,
340+
action: "nav",
341+
view: params.view,
342+
provider: params.provider,
343+
runtime: params.runtime,
344+
page: Math.min(params.totalPages, params.page + 1),
345+
providerPage: params.providerPage,
346+
modelIndex: params.modelIndex,
347+
userId: params.userId,
348+
}),
349+
});
350+
return new Row([prevButton, indicatorButton, nextButton]);
351+
}
352+
295353
function buildModelRows(params: {
296354
command: DiscordModelPickerCommandContext;
297355
userId: string;
@@ -415,6 +473,23 @@ function buildModelRows(params: {
415473
]),
416474
);
417475

476+
const modelNavRow = buildPaginationRow({
477+
command: params.command,
478+
userId: params.userId,
479+
view: "models",
480+
page: params.modelPage.page,
481+
totalPages: params.modelPage.totalPages,
482+
hasPrev: params.modelPage.hasPrev,
483+
hasNext: params.modelPage.hasNext,
484+
provider: params.modelPage.provider,
485+
runtime: stateRuntime,
486+
providerPage: providerPage.page,
487+
modelIndex: params.pendingModelIndex,
488+
});
489+
if (modelNavRow) {
490+
rows.push(modelNavRow);
491+
}
492+
418493
const resolvedDefault = params.data.resolvedDefault;
419494
const shouldDisableReset =
420495
Boolean(parsedCurrentModel) &&
@@ -505,23 +580,40 @@ export function renderDiscordModelPickerProvidersView(
505580
): DiscordModelPickerRenderedView {
506581
const page = getDiscordModelPickerProviderPage({ data: params.data, page: params.page });
507582
const parsedCurrent = parseCurrentModelRef(params.currentModel);
508-
const rows = buildProviderRows({
583+
const rows: DiscordModelPickerRow[] = buildProviderRows({
509584
command: params.command,
510585
userId: params.userId,
511586
page,
512587
currentProvider: parsedCurrent?.provider,
513588
});
514589

590+
const navRow = buildPaginationRow({
591+
command: params.command,
592+
userId: params.userId,
593+
view: "providers",
594+
page: page.page,
595+
totalPages: page.totalPages,
596+
hasPrev: page.hasPrev,
597+
hasNext: page.hasNext,
598+
});
599+
if (navRow) {
600+
rows.push(navRow);
601+
}
602+
515603
const detailLines = [
516604
formatCurrentModelLine(params.currentModel),
517605
`Select a provider (${page.totalItems} available).`,
518606
];
607+
const footer =
608+
page.totalPages > 1
609+
? `Showing page ${page.page}/${page.totalPages} · ${page.totalItems} providers total`
610+
: `All ${page.totalItems} providers shown`;
519611
return buildRenderedShell({
520612
layout: params.layout ?? "v2",
521613
title: "Model Picker",
522614
detailLines,
523615
rows,
524-
footer: `All ${page.totalItems} providers shown`,
616+
footer,
525617
});
526618
}
527619

@@ -587,10 +679,17 @@ export function renderDiscordModelPickerModelsView(
587679
})} (press Submit)`
588680
: "Select a model, then press Submit.";
589681

682+
const detailLines = [formatCurrentModelLine(params.currentModel), `Default: ${defaultModel}`];
683+
if (modelPage.totalPages > 1) {
684+
detailLines.push(
685+
`${modelPage.provider}: page ${modelPage.page}/${modelPage.totalPages} · ${modelPage.totalItems} models`,
686+
);
687+
}
688+
590689
return buildRenderedShell({
591690
layout: params.layout ?? "v2",
592691
title: "Model Picker",
593-
detailLines: [formatCurrentModelLine(params.currentModel), `Default: ${defaultModel}`],
692+
detailLines,
594693
preRowText: pendingLine,
595694
rows,
596695
trailingRows: [buttonRow],

extensions/discord/src/monitor/native-command-model-picker-interaction.ts

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -258,6 +258,46 @@ export async function handleDiscordModelPickerInteraction(params: {
258258
return;
259259
}
260260

261+
if (parsed.action === "nav" && parsed.view === "providers") {
262+
const rendered = renderDiscordModelPickerProvidersView({
263+
command: parsed.command,
264+
userId: parsed.userId,
265+
data: pickerData,
266+
page: parsed.page,
267+
currentModel: currentModelRef,
268+
});
269+
await updatePicker(toDiscordModelPickerMessagePayload(rendered));
270+
return;
271+
}
272+
273+
if (parsed.action === "nav" && parsed.view === "models") {
274+
const provider =
275+
parsed.provider ??
276+
splitDiscordModelRef(currentModelRef ?? "")?.provider ??
277+
pickerData.resolvedDefault.provider;
278+
const pendingModel = resolveDiscordModelPickerModelByIndex({
279+
data: pickerData,
280+
provider,
281+
modelIndex: parsed.modelIndex,
282+
});
283+
const rendered = renderDiscordModelPickerModelsView({
284+
command: parsed.command,
285+
userId: parsed.userId,
286+
data: pickerData,
287+
provider,
288+
page: parsed.page,
289+
providerPage: parsed.providerPage ?? 1,
290+
currentModel: currentModelRef,
291+
currentRuntime,
292+
...(pendingModel ? { pendingModel: `${provider}/${pendingModel}` } : {}),
293+
pendingModelIndex: parsed.modelIndex,
294+
pendingRuntime: parsed.runtime,
295+
quickModels,
296+
});
297+
await updatePicker(toDiscordModelPickerMessagePayload(rendered));
298+
return;
299+
}
300+
261301
if (parsed.action === "back" && parsed.view === "models") {
262302
const provider =
263303
parsed.provider ??

extensions/discord/src/monitor/native-command-model-picker-ui.ts

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ import {
2727
import {
2828
loadDiscordModelPickerData,
2929
renderDiscordModelPickerModelsView,
30+
resolveDiscordModelPickerPageForModel,
3031
toDiscordModelPickerMessagePayload,
3132
type DiscordModelPickerCommandContext,
3233
} from "./model-picker.js";
@@ -311,18 +312,26 @@ export async function replyWithDiscordModelPickerProviders(params: {
311312
allowedModelRefs: buildDiscordModelPickerAllowedModelRefs(data),
312313
limit: 5,
313314
});
314-
const currentProvider = splitDiscordModelRef(currentModel ?? "")?.provider;
315+
const parsedCurrentRef = splitDiscordModelRef(currentModel ?? "");
315316
const initialProvider =
316-
currentProvider && data.byProvider.has(currentProvider)
317-
? currentProvider
317+
parsedCurrentRef && data.byProvider.has(parsedCurrentRef.provider)
318+
? parsedCurrentRef.provider
318319
: (data.providers[0] ?? data.resolvedDefault.provider);
320+
const initialPage =
321+
parsedCurrentRef && parsedCurrentRef.provider === initialProvider
322+
? resolveDiscordModelPickerPageForModel({
323+
data,
324+
provider: initialProvider,
325+
model: parsedCurrentRef.model,
326+
})
327+
: 1;
319328

320329
const rendered = renderDiscordModelPickerModelsView({
321330
command: params.command,
322331
userId: params.userId,
323332
data,
324333
provider: initialProvider,
325-
page: 1,
334+
page: initialPage,
326335
providerPage: 1,
327336
currentModel,
328337
currentRuntime,

0 commit comments

Comments
 (0)