diff --git a/config/quality/eslint-suppressions.json b/config/quality/eslint-suppressions.json index 0cab8df64d..47f146c9c6 100644 --- a/config/quality/eslint-suppressions.json +++ b/config/quality/eslint-suppressions.json @@ -1132,11 +1132,6 @@ "count": 4 } }, - "tests/unit/copilot-gemini-claude-route-no-responses.test.ts": { - "@typescript-eslint/no-explicit-any": { - "count": 9 - } - }, "tests/unit/cursor-usage-fetcher.test.ts": { "@typescript-eslint/no-explicit-any": { "count": 1 diff --git a/config/quality/file-size-baseline.json b/config/quality/file-size-baseline.json index 0b2beb0367..b589c177cb 100644 --- a/config/quality/file-size-baseline.json +++ b/config/quality/file-size-baseline.json @@ -206,7 +206,7 @@ "src/app/(dashboard)/dashboard/providers/[id]/hooks/useProviderConnections.ts": 954, "src/app/(dashboard)/dashboard/providers/[id]/hooks/useProviderModels.ts": 155, "src/app/(dashboard)/dashboard/providers/[id]/hooks/useProviderSettings.ts": 264, - "src/app/(dashboard)/dashboard/providers/[id]/providerPageHelpers.ts": 1021, + "src/app/(dashboard)/dashboard/providers/[id]/providerPageHelpers.ts": 1034, "src/app/(dashboard)/dashboard/providers/components/onboarding/ProviderOnboardingWizard.tsx": 906, "src/app/(dashboard)/dashboard/providers/page.tsx": 1927, "src/app/(dashboard)/dashboard/runtime/RuntimePageClient.tsx": 1201, @@ -372,5 +372,6 @@ "_rebaseline_2026_06_25_rc17_pr_batch": "rc17 PR batch own growth (cohesive, not extractable): responseSanitizer.ts 1103->1122 (+19 = SanitizeOpenAIResponseOptions interface + stripReasoning option, #4678); tokenRefresh.ts 2070->2090 (+20 = codex 401 defense-in-depth unrecoverable-refresh guard, #4686); token-refresh-service.test.ts 1322->1353 (+31 = 401-unfamiliar-payload regression case, #4686); translator-openai-responses-req.test.ts 1047->1050 (+3 = reasoning_effort non-Copilot assertion update, #4688). All are the merged PRs own surgical additions at existing chokepoints.", "_rebaseline_2026_06_25_rc17b_leva2": "rc17 leva2 PR batch own growth (cohesive, not extractable): providerLimits.ts 950->955 (#4786 generalized accesstoken fallback); default.ts NEW frozen entry at 828 (#4729 anthropic-compatible Bearer + #4766 json_schema fallback + #4787 cline workos headers — three provider-specific header branches); openai-to-kiro.ts 807->814 (#4763 Claude-capability image gate); openai-responses.ts 923->937 (#4764 computeFinishReason guard); executor-default-base.test.ts 1339->1440 (#4766 json_schema fallback tests); translator-openai-to-kiro.test.ts 918->980 (#4763 non-Claude image gate tests).", "_rebaseline_2026_06_27_v3838_filesize_drift": "Mid-cycle drift on release/v3.8.38 — feature/fix growth from already-merged PRs that the fast-path (PR->release skips check:file-size) let accumulate without a bump. src/shared/constants/sidebarVisibility.ts 1100->1198 (+98 = #3812 colored menu-icon support, per-item accent map across the sidebar entries; #5142 then dropped one orphan settings entry, net still above the frozen). src/sse/handlers/chat.ts 1560->1575 (+15 = #5064 self-inflicted-timeout cooldown skip + #5124 long OpenAI-compatible SSE hardening + #5110 embed-WS LIVE_WS_HOST honour / early empty-message reject). Localized feature/fix code next to existing branches, each covered by its own PR tests; not extractable without hiding the chokepoint. Structural shrink of chat.ts tracked in #3501.", - "_rebaseline_2026_07_05_6211_providerlimits_fetch_timeout": "PR #6211 own growth: src/app/(dashboard)/dashboard/usage/components/ProviderLimits/index.tsx 1121->1127 (+6 = data-timeout guard for the quota page's two first-paint fetches — a PROVIDER_LIMITS_FETCH_TIMEOUT_MS const + fetchWithTimeout at the /api/providers/client and /api/usage/provider-limits call sites — so a never-settling connection can no longer wedge initialLoading on the skeleton, same infinite-skeleton class the PR also fixes on the providers page). Cohesive fix code at the existing fetch chokepoints (the 5-line rationale comment explains why a timeout/abort is needed where a try/catch only rescues a rejection); not extractable. Covered by tests/unit/providers-page-data-timeout.test.ts. Fast-path PR->release skips check:file-size, so this bump lands with the PR. Structural shrink of this file tracked in #3501." + "_rebaseline_2026_07_05_6211_providerlimits_fetch_timeout": "PR #6211 own growth: src/app/(dashboard)/dashboard/usage/components/ProviderLimits/index.tsx 1121->1127 (+6 = data-timeout guard for the quota page's two first-paint fetches — a PROVIDER_LIMITS_FETCH_TIMEOUT_MS const + fetchWithTimeout at the /api/providers/client and /api/usage/provider-limits call sites — so a never-settling connection can no longer wedge initialLoading on the skeleton, same infinite-skeleton class the PR also fixes on the providers page). Cohesive fix code at the existing fetch chokepoints (the 5-line rationale comment explains why a timeout/abort is needed where a try/catch only rescues a rejection); not extractable. Covered by tests/unit/providers-page-data-timeout.test.ts. Fast-path PR->release skips check:file-size, so this bump lands with the PR. Structural shrink of this file tracked in #3501.", + "_rebaseline_2026_07_05_6154_copilot_catalog_helpers": "PR #6154 own growth: src/app/(dashboard)/dashboard/providers/[id]/providerPageHelpers.ts 1021->1034 (+13 = GitHub Copilot catalog refresh — model-section helper wiring for the refreshed passthrough/compatible model lists). Cohesive UI-helper growth alongside the registry/modelSpecs catalog refresh; not extractable. Covered by the PR's provider-registry-github-copilot-* unit tests. Fast-path PR->release skips check:file-size, so this bump lands with the PR (contributor backryun)." } diff --git a/open-sse/config/providers/registry/github/index.ts b/open-sse/config/providers/registry/github/index.ts index f8c25d05a1..41673dcf7d 100644 --- a/open-sse/config/providers/registry/github/index.ts +++ b/open-sse/config/providers/registry/github/index.ts @@ -25,69 +25,137 @@ export const githubProvider: RegistryEntry = { defaultContextLength: 128000, headers: getGitHubCopilotChatHeaders(), models: [ - // Copilot still serves the original GPT-4 via chat/completions; keep it - // alongside GPT-4o and the GPT-5.x family so apps that hard-code `gpt-4` resolve here. - { id: "gpt-4", name: "GPT-4", contextLength: 128000 }, - // 9router#98 — Copilot still serves GPT-4o via chat/completions; keep it - // alongside the GPT-5.x family so apps that hard-code `gpt-4o` resolve here. - { id: "gpt-4o", name: "GPT-4o", contextLength: 128000 }, - // Copilot also serves the cheaper GPT-4o mini via chat/completions; keep it - // alongside gpt-4o so apps that hard-code `gpt-4o-mini` resolve to the Copilot - // (`gh`) provider rather than only the github-models (`ghm`) marketplace entry. - { id: "gpt-4o-mini", name: "GPT-4o mini", contextLength: 128000 }, - { id: "gpt-5-mini", name: "GPT-5 Mini", targetFormat: "openai-responses" }, - { id: "gpt-5.3-codex", name: "GPT-5.3 Codex", targetFormat: "openai-responses" }, - { id: "gpt-5.4-mini", name: "GPT-5.4 Mini", targetFormat: "openai-responses" }, { - id: "gpt-5.4", - name: "GPT-5.4", - targetFormat: "openai-responses", - supportsXHighEffort: true, + id: "claude-fable-5", + name: "Claude Fable 5", + contextLength: 1000000, + maxOutputTokens: 64000, }, - { id: "gpt-5.5", name: "GPT-5.5", ...GPT_5_5_CODEX_CAPABILITIES }, { - id: "claude-haiku-4.5", - name: "Claude Haiku 4.5", + id: "claude-opus-4.8-fast", + name: "Claude Opus 4.8 (fast mode)", + contextLength: 1000000, + maxOutputTokens: 64000, + unsupportedParams: ["temperature", "top_p", "top_k"], + }, + { + id: "claude-opus-4.8", + name: "Claude Opus 4.8", + contextLength: 1000000, + maxOutputTokens: 64000, + unsupportedParams: ["temperature", "top_p", "top_k"], + }, + { + id: "claude-opus-4.7", + name: "Claude Opus 4.7", + contextLength: 1000000, + maxOutputTokens: 64000, + }, + { + id: "claude-sonnet-4.6", + name: "Claude Sonnet 4.6", + contextLength: 1000000, + maxOutputTokens: 64000, + }, + { + id: "claude-opus-4.5", + name: "Claude Opus 4.5", contextLength: 200000, + maxOutputTokens: 32000, + }, + { + id: "claude-sonnet-5", + name: "Claude Sonnet 5", + contextLength: 1000000, maxOutputTokens: 64000, }, { id: "claude-sonnet-4.5", name: "Claude Sonnet 4.5", contextLength: 200000, - maxOutputTokens: 64000, + maxOutputTokens: 32000, }, { - id: "claude-sonnet-4.6", - name: "Claude Sonnet 4.6", + id: "claude-haiku-4.5", + name: "Claude Haiku 4.5", contextLength: 200000, - maxOutputTokens: 64000, - }, - { - // #2911: GitHub Copilot's Responses API does not serve Claude/Gemini — - // route them via chat/completions (provider default) like claude-opus-4.6. - id: "claude-opus-4-5-20251101", - name: "Claude Opus 4.5 (Full ID)", - contextLength: 200000, - maxOutputTokens: 64000, - }, - { - id: "claude-opus-4.6", - name: "Claude Opus 4.6", - contextLength: 1000000, - maxOutputTokens: 128000, - }, - { - // #2911: Claude on Copilot must use chat/completions, not the Responses API. - id: "claude-opus-4.7", - name: "Claude Opus 4.7", - contextLength: 1000000, - maxOutputTokens: 128000, + maxOutputTokens: 32000, }, // #2911: Gemini on Copilot must use chat/completions, not the Responses API. - { id: "gemini-3.1-pro-preview", name: "Gemini 3.1 Pro" }, - { id: "gemini-3-flash-preview", name: "Gemini 3 Flash" }, - { id: "oswe-vscode-prime", name: "Raptor Mini", targetFormat: "openai-responses" }, - //{ id: "?", name: "Goldeneye" }, + { + id: "gemini-3.1-pro-preview", + name: "Gemini 3.1 Pro", + contextLength: 1000000, + maxOutputTokens: 64000, + }, + { + id: "gemini-3.5-flash", + name: "Gemini 3.5 Flash", + contextLength: 1000000, + maxOutputTokens: 64000, + }, + { id: "gpt-5.5", name: "GPT-5.5", ...GPT_5_5_CODEX_CAPABILITIES, maxOutputTokens: 128000 }, + { + id: "gpt-5.4", + name: "GPT-5.4", + targetFormat: "openai-responses", + supportsXHighEffort: true, + contextLength: 1050000, + maxOutputTokens: 128000, + }, + { + id: "gpt-5.4-mini", + name: "GPT-5.4 mini", + targetFormat: "openai-responses", + contextLength: 400000, + maxOutputTokens: 128000, + }, + { + id: "gpt-5.3-codex", + name: "GPT-5.3-Codex", + targetFormat: "openai-responses", + contextLength: 400000, + maxOutputTokens: 128000, + }, + { + id: "gpt-5-mini", + name: "GPT-5 mini", + targetFormat: "openai-responses", + contextLength: 264000, + maxOutputTokens: 64000, + }, + { + id: "gpt-4o-2024-11-20", + name: "GPT-4o", + contextLength: 128000, + maxOutputTokens: 16384, + }, + { id: "gpt-4o-mini", name: "GPT-4o mini", contextLength: 128000, maxOutputTokens: 4096 }, + { + id: "gpt-4-0125-preview", + name: "GPT 4 Turbo", + contextLength: 128000, + maxOutputTokens: 4096, + }, + { + id: "kimi-k2.7-code", + name: "Kimi K2.7 Code", + contextLength: 256000, + maxOutputTokens: 32000, + }, + { + id: "mai-code-1-flash", + name: "MAI-Code-1-Flash", + targetFormat: "openai-responses", + contextLength: 256000, + maxOutputTokens: 128000, + }, + { + id: "oswe-vscode-prime", + name: "Raptor mini", + targetFormat: "openai-responses", + contextLength: 264000, + maxOutputTokens: 64000, + }, ], }; diff --git a/open-sse/services/githubCopilotModels.ts b/open-sse/services/githubCopilotModels.ts index b24a57c3e8..5ccbcb623b 100644 --- a/open-sse/services/githubCopilotModels.ts +++ b/open-sse/services/githubCopilotModels.ts @@ -20,6 +20,32 @@ import { getGitHubCopilotChatHeaders } from "../config/providerHeaderProfiles.ts"; export const GITHUB_COPILOT_MODELS_URL = "https://api.githubcopilot.com/models"; +export const GITHUB_COPILOT_MODEL_ALLOWLIST = [ + "claude-fable-5", + "claude-opus-4.8-fast", + "claude-opus-4.8", + "claude-opus-4.7", + "claude-sonnet-4.6", + "claude-opus-4.5", + "claude-sonnet-5", + "claude-sonnet-4.5", + "claude-haiku-4.5", + "gemini-3.1-pro-preview", + "gemini-3.5-flash", + "gpt-5.5", + "gpt-5.4", + "gpt-5.4-mini", + "gpt-5.3-codex", + "gpt-5-mini", + "gpt-4o-2024-11-20", + "gpt-4o-mini", + "gpt-4-0125-preview", + "kimi-k2.7-code", + "mai-code-1-flash", + "oswe-vscode-prime", +] as const; + +const GITHUB_COPILOT_MODEL_ALLOWLIST_SET = new Set(GITHUB_COPILOT_MODEL_ALLOWLIST); export type GitHubCopilotModel = { id: string; @@ -59,6 +85,7 @@ export function parseGitHubCopilotModels(data: unknown): GitHubCopilotModel[] { const item = asRecord(value); const id = toNonEmptyString(item.id) || toNonEmptyString(item.model); if (!id || seen.has(id)) continue; + if (!GITHUB_COPILOT_MODEL_ALLOWLIST_SET.has(id)) continue; seen.add(id); const name = toNonEmptyString(item.name) || toNonEmptyString(item.display_name) || id; models.push({ id, name, owned_by: "github" }); @@ -89,6 +116,7 @@ function toFallbackResult( .map((model) => { const id = toNonEmptyString(model.id); if (!id) return null; + if (!GITHUB_COPILOT_MODEL_ALLOWLIST_SET.has(id)) return null; return { id, name: toNonEmptyString(model.name) || id, owned_by: "github" }; }) .filter((model): model is GitHubCopilotModel => Boolean(model)); diff --git a/src/app/(dashboard)/dashboard/providers/[id]/__tests__/phase1e.test.tsx b/src/app/(dashboard)/dashboard/providers/[id]/__tests__/phase1e.test.tsx index fb0562c8a8..a65bad6019 100644 --- a/src/app/(dashboard)/dashboard/providers/[id]/__tests__/phase1e.test.tsx +++ b/src/app/(dashboard)/dashboard/providers/[id]/__tests__/phase1e.test.tsx @@ -15,6 +15,7 @@ import { describe, it, expect, vi, beforeEach, afterEach } from "vitest"; import { buildCompatMap, isModelHiddenFn, + getDisplayModelAlias, effectiveNormalizeForProtocol, effectivePreserveForProtocol, anyNormalizeCompatBadge, @@ -35,10 +36,7 @@ vi.mock("next/navigation", () => ({ vi.mock("next-intl", () => ({ useTranslations: () => (key: string, values?: Record) => { if (values) { - return Object.entries(values).reduce( - (acc, [k, v]) => acc.replace(`{${k}}`, String(v)), - key - ); + return Object.entries(values).reduce((acc, [k, v]) => acc.replace(`{${k}}`, String(v)), key); } return key; }, @@ -85,6 +83,22 @@ describe("providerPageHelpers — model-compat pure functions", () => { expect(isModelHiddenFn("unknown-model", customMap, overrideMap)).toBe(false); }); + it("isModelHiddenFn ignores deleted tombstones when reading visibility", () => { + const customMap = buildCompatMap([]); + const overrideMap = buildCompatMap([ + { id: "gpt-4o-2024-11-20", isHidden: true, isDeleted: true }, + { id: "gpt-5-mini", isHidden: true }, + ]); + + expect(isModelHiddenFn("gpt-4o-2024-11-20", customMap, overrideMap)).toBe(false); + expect(isModelHiddenFn("gpt-5-mini", customMap, overrideMap)).toBe(true); + }); + + it("getDisplayModelAlias ignores provider-scoped identity aliases", () => { + expect(getDisplayModelAlias("gpt-4o-2024-11-20", "gpt-4o-2024-11-20")).toBeNull(); + expect(getDisplayModelAlias("gpt-5-mini", "fast-mini")).toBe("fast-mini"); + }); + it("effectiveNormalizeForProtocol returns correct flag", () => { const customMap = buildCompatMap(customModels); const overrideMap = buildCompatMap(overrideModels); @@ -115,10 +129,10 @@ describe("providerPageHelpers — model-compat pure functions", () => { }); it("formatProviderModelsErrorResponse extracts error.message", async () => { - const mockRes = new Response( - JSON.stringify({ error: { message: "Model not found" } }), - { status: 422, statusText: "Unprocessable Entity" } - ); + const mockRes = new Response(JSON.stringify({ error: { message: "Model not found" } }), { + status: 422, + statusText: "Unprocessable Entity", + }); const detail = await formatProviderModelsErrorResponse(mockRes); expect(detail).toBe("Model not found"); }); @@ -185,7 +199,9 @@ describe("PassthroughModelRow — render smoke test", () => { }); afterEach(() => { - act(() => { root.unmount(); }); + act(() => { + root.unmount(); + }); container.remove(); }); @@ -222,7 +238,9 @@ describe("ModelVisibilityToolbar — render smoke test", () => { }); afterEach(() => { - act(() => { root.unmount(); }); + act(() => { + root.unmount(); + }); container.remove(); }); @@ -259,7 +277,9 @@ describe("useModelCompatState — hook unit test via component wrapper", () => { }); afterEach(() => { - act(() => { root.unmount(); }); + act(() => { + root.unmount(); + }); container.remove(); }); @@ -267,7 +287,12 @@ describe("useModelCompatState — hook unit test via component wrapper", () => { const { useModelCompatState } = await import("../hooks/useModelCompatState"); const customModels = [ - { id: "gpt-4o", normalizeToolCallId: true, preserveOpenAIDeveloperRole: false, isHidden: true }, + { + id: "gpt-4o", + normalizeToolCallId: true, + preserveOpenAIDeveloperRole: false, + isHidden: true, + }, ]; const modelCompatOverrides: any[] = []; @@ -281,7 +306,9 @@ describe("useModelCompatState — hook unit test via component wrapper", () => { compat.effectiveModelPreserveDeveloper("gpt-4o"), compat.anyNormalizeCompatBadge("gpt-4o"), compat.anyNoPreserveCompatBadge("gpt-4o"), - ].map(String).join(","); + ] + .map(String) + .join(","); return {results}; } @@ -291,8 +318,9 @@ describe("useModelCompatState — hook unit test via component wrapper", () => { const span = container.querySelector("[data-testid='results']"); expect(span).not.toBeNull(); - const [hidden, notHidden, normalize, preserve, anyNorm, anyNoPreserve] = - (span!.textContent ?? "").split(","); + const [hidden, notHidden, normalize, preserve, anyNorm, anyNoPreserve] = ( + span!.textContent ?? "" + ).split(","); expect(hidden).toBe("true"); expect(notHidden).toBe("false"); diff --git a/src/app/(dashboard)/dashboard/providers/[id]/components/CompatibleModelsSection.tsx b/src/app/(dashboard)/dashboard/providers/[id]/components/CompatibleModelsSection.tsx index 94c1307175..7a8542265b 100644 --- a/src/app/(dashboard)/dashboard/providers/[id]/components/CompatibleModelsSection.tsx +++ b/src/app/(dashboard)/dashboard/providers/[id]/components/CompatibleModelsSection.tsx @@ -17,6 +17,7 @@ import { resolveManagedModelAlias } from "@/shared/utils/providerModelAliases"; import { useNotificationStore } from "@/store/notificationStore"; import { buildCompatMap, + getDisplayModelAlias, providerText, type CompatModelRow, } from "../providerPageHelpers"; @@ -57,10 +58,7 @@ export interface CompatibleModelsSectionProps { effectiveModelNormalize: (alias: string) => boolean; effectiveModelPreserveDeveloper: (alias: string) => boolean; getUpstreamHeadersRecord: (modelId: string, protocol: string) => Record; - saveModelCompatFlags: ( - modelId: string, - flags: CompatibleModelsSaveFlags - ) => Promise; + saveModelCompatFlags: (modelId: string, flags: CompatibleModelsSaveFlags) => Promise; compatSavingModelId?: string; onModelsChanged?: () => void; isModelHidden: (modelId: string) => boolean; @@ -155,7 +153,8 @@ export default function CompatibleModelsSection({ for (const [alias, fullModel] of providerAliases) { const fmStr = fullModel as string; const modelId = fmStr.startsWith(prefix) ? fmStr.slice(prefix.length) : fmStr; - aliasByModelId.set(modelId, alias as string); + const displayAlias = getDisplayModelAlias(modelId, alias as string); + if (displayAlias) aliasByModelId.set(modelId, displayAlias); } const addModel = (model: CompatModelRow, source: string) => { @@ -194,11 +193,13 @@ export default function CompatibleModelsSection({ const fmStr = fullModel as string; const modelId = fmStr.startsWith(prefix) ? fmStr.slice(prefix.length) : fmStr; if (!modelId || seenModelIds.has(modelId)) continue; + const displayAlias = getDisplayModelAlias(modelId, alias as string); + if (!displayAlias) continue; const customModel = customModelMap.get(modelId); rows.push({ modelId, - alias: alias as string, - displayName: alias as string, + alias: displayAlias, + displayName: displayAlias, source: customModel ? customModel.source || "custom" : "alias", isFree: modelId.endsWith(":free") || diff --git a/src/app/(dashboard)/dashboard/providers/[id]/components/PassthroughModelsSection.tsx b/src/app/(dashboard)/dashboard/providers/[id]/components/PassthroughModelsSection.tsx index e3dda0a620..fb0d2b6e62 100644 --- a/src/app/(dashboard)/dashboard/providers/[id]/components/PassthroughModelsSection.tsx +++ b/src/app/(dashboard)/dashboard/providers/[id]/components/PassthroughModelsSection.tsx @@ -21,6 +21,7 @@ import { import { useNotificationStore } from "@/store/notificationStore"; import { buildCompatMap, + getDisplayModelAlias, providerText, testAllResultsText, evaluateTestAllEntry, @@ -228,7 +229,8 @@ export default function PassthroughModelsSection({ for (const [alias, fullModel] of providerAliases) { const fmStr = fullModel as string; const modelId = fmStr.startsWith(prefix) ? fmStr.slice(prefix.length) : fmStr; - aliasByModelId.set(modelId, alias as string); + const displayAlias = getDisplayModelAlias(modelId, alias as string); + if (displayAlias) aliasByModelId.set(modelId, displayAlias); fullModelByModelId.set(modelId, fmStr); } @@ -266,12 +268,14 @@ export default function PassthroughModelsSection({ const fmStr = fullModel as string; const modelId = fmStr.startsWith(prefix) ? fmStr.slice(prefix.length) : fmStr; if (!modelId || seenModelIds.has(modelId)) continue; + const displayAlias = getDisplayModelAlias(modelId, alias as string); + if (!displayAlias) continue; const customModel = customModelMap.get(modelId); rows.push({ modelId, fullModel: fmStr, - alias: alias as string, - displayName: alias as string, + alias: displayAlias, + displayName: displayAlias, source: customModel ? customModel.source || "custom" : "alias", isFree: modelId.endsWith(":free") || diff --git a/src/app/(dashboard)/dashboard/providers/[id]/components/ProviderModelsSection.tsx b/src/app/(dashboard)/dashboard/providers/[id]/components/ProviderModelsSection.tsx index c2edb4b633..1b252b209e 100644 --- a/src/app/(dashboard)/dashboard/providers/[id]/components/ProviderModelsSection.tsx +++ b/src/app/(dashboard)/dashboard/providers/[id]/components/ProviderModelsSection.tsx @@ -15,7 +15,11 @@ import { useState } from "react"; import { Button } from "@/shared/components"; import { matchesModelCatalogQuery } from "@/shared/utils/modelCatalogSearch"; import { isFreeModel, sortModelsFreeFirst } from "@/shared/utils/freeModels"; -import { providerText, type ProviderMessageTranslator } from "../providerPageHelpers"; +import { + getDisplayModelAlias, + providerText, + type ProviderMessageTranslator, +} from "../providerPageHelpers"; import ModelRow, { ModelVisibilityToolbar } from "./ModelRow"; import PassthroughModelsSection from "./PassthroughModelsSection"; import CompatibleModelsSection from "./CompatibleModelsSection"; @@ -86,11 +90,7 @@ export interface ProviderModelsSectionProps { setAutoHideFailed: (v: boolean) => void; setVisibilityFilter: (v: "all" | "visible" | "hidden") => void; saveModelCompatFlags: (modelId: string, patch: ModelCompatSavePatch) => Promise; - handleToggleModelHidden: ( - providerKey: string, - modelId: string, - hidden: boolean - ) => Promise; + handleToggleModelHidden: (providerKey: string, modelId: string, hidden: boolean) => Promise; handleBulkToggleModelHidden: ( providerKey: string, modelIds: string[], @@ -187,8 +187,7 @@ export default function ProviderModelsSection({ ); - const clearAllButton = (modelMeta.customModels.length > 0 || - providerAliasEntries.length > 0) && ( + const clearAllButton = (modelMeta.customModels.length > 0 || providerAliasEntries.length > 0) && (