From 91d9573b55d5848f3b0a7e655f62fba34d835e6c Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Sat, 14 Mar 2026 01:23:24 +0000 Subject: [PATCH] refactor: declone model picker model ref parsing --- .../src/mattermost/model-picker.test.ts | 9 +++++++++ .../mattermost/src/mattermost/model-picker.ts | 12 +++++------- src/discord/monitor/model-picker.test.ts | 18 ++++++++++++++++++ src/discord/monitor/model-picker.ts | 13 ++++++------- 4 files changed, 38 insertions(+), 14 deletions(-) diff --git a/extensions/mattermost/src/mattermost/model-picker.test.ts b/extensions/mattermost/src/mattermost/model-picker.test.ts index b448339523e..cebafc4a1bc 100644 --- a/extensions/mattermost/src/mattermost/model-picker.test.ts +++ b/extensions/mattermost/src/mattermost/model-picker.test.ts @@ -60,6 +60,15 @@ describe("Mattermost model picker", () => { expect(view.buttons[0]?.[0]?.text).toBe("Browse providers"); }); + it("trims accidental model spacing in Mattermost current-model text", () => { + const view = renderMattermostModelSummaryView({ + ownerUserId: "user-1", + currentModel: " OpenAI/ gpt-5 ", + }); + + expect(view.text).toContain("Current: openai/gpt-5"); + }); + it("renders providers and models with Telegram-style navigation", () => { const providersView = renderMattermostProviderPickerView({ ownerUserId: "user-1", diff --git a/extensions/mattermost/src/mattermost/model-picker.ts b/extensions/mattermost/src/mattermost/model-picker.ts index 42462180901..1547041a74a 100644 --- a/extensions/mattermost/src/mattermost/model-picker.ts +++ b/extensions/mattermost/src/mattermost/model-picker.ts @@ -36,15 +36,13 @@ export type MattermostModelPickerRenderedView = { function splitModelRef(modelRef?: string | null): { provider: string; model: string } | null { const trimmed = modelRef?.trim(); - if (!trimmed) { + const match = trimmed?.match(/^([^/]+)\/(.+)$/u); + if (!match) { return null; } - const slashIndex = trimmed.indexOf("/"); - if (slashIndex <= 0 || slashIndex >= trimmed.length - 1) { - return null; - } - const provider = normalizeProviderId(trimmed.slice(0, slashIndex)); - const model = trimmed.slice(slashIndex + 1).trim(); + const provider = normalizeProviderId(match[1]); + // Mattermost copy should normalize accidental whitespace around the model. + const model = match[2].trim(); if (!provider || !model) { return null; } diff --git a/src/discord/monitor/model-picker.test.ts b/src/discord/monitor/model-picker.test.ts index 04d5006feb6..834fc4ff124 100644 --- a/src/discord/monitor/model-picker.test.ts +++ b/src/discord/monitor/model-picker.test.ts @@ -415,6 +415,24 @@ describe("Discord model picker rendering", () => { expect(payload.components?.[0]?.type).toBe(ComponentType.ActionRow); }); + it("preserves the stored model suffix spacing in Discord current-model text", () => { + const data = createModelsProviderData({ openai: [" gpt-5", "gpt-4o"] }); + + const rendered = renderDiscordModelPickerProvidersView({ + command: "model", + userId: "99", + data, + currentModel: " OpenAI/ gpt-5 ", + layout: "classic", + }); + + const payload = serializePayload(toDiscordModelPickerMessagePayload(rendered)) as { + content?: string; + }; + + expect(payload.content).toContain("Current model: openai/ gpt-5"); + }); + it("renders model view with select menu and explicit submit button", () => { const data = createModelsProviderData({ openai: ["gpt-4.1", "gpt-4o", "o3"], diff --git a/src/discord/monitor/model-picker.ts b/src/discord/monitor/model-picker.ts index 9fa8063cb9a..7d552d38650 100644 --- a/src/discord/monitor/model-picker.ts +++ b/src/discord/monitor/model-picker.ts @@ -233,15 +233,14 @@ function paginateItems(params: { function parseCurrentModelRef(raw?: string): DiscordModelPickerCurrentModelRef | null { const trimmed = raw?.trim(); - if (!trimmed) { + const match = trimmed?.match(/^([^/]+)\/(.+)$/u); + if (!match) { return null; } - const slashIndex = trimmed.indexOf("/"); - if (slashIndex <= 0 || slashIndex >= trimmed.length - 1) { - return null; - } - const provider = normalizeProviderId(trimmed.slice(0, slashIndex)); - const model = trimmed.slice(slashIndex + 1); + const provider = normalizeProviderId(match[1]); + // Preserve the model suffix exactly as entered after "/" so select defaults + // continue to mirror the stored ref for Discord interactions. + const model = match[2]; if (!provider || !model) { return null; }